diff --git a/samples/subsys/ipc/basic/CMakeLists.txt b/samples/subsys/ipc/basic/CMakeLists.txt new file mode 100644 index 0000000000000..0e88ed0751e21 --- /dev/null +++ b/samples/subsys/ipc/basic/CMakeLists.txt @@ -0,0 +1,21 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 +# + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) + +if(NOT CONFIG_BOARD_NRF54L15PDK_NRF54L15_CPUAPP) + message(FATAL_ERROR "${BOARD} is not supported for this sample") +endif() + +project(ipc_basic_host) + +target_include_directories(app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/common) + +target_sources(app PRIVATE src/main.c) + +include(ExternalProject) diff --git a/samples/subsys/ipc/basic/Kconfig.sysbuild b/samples/subsys/ipc/basic/Kconfig.sysbuild new file mode 100644 index 0000000000000..29d72164417e9 --- /dev/null +++ b/samples/subsys/ipc/basic/Kconfig.sysbuild @@ -0,0 +1,9 @@ +# Copyright 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 + +source "share/sysbuild/Kconfig" + +config REMOTE_BOARD +string + default "nrf54l15pdk/nrf54l15/cpuflpr" if $(BOARD) = "nrf54l15pdk" diff --git a/samples/subsys/ipc/basic/README.rst b/samples/subsys/ipc/basic/README.rst new file mode 100644 index 0000000000000..f9cf2e53225a6 --- /dev/null +++ b/samples/subsys/ipc/basic/README.rst @@ -0,0 +1,48 @@ +.. zephyr:code-sample:: ipc-basic + :name: IPC service: basic struct + :relevant-api: ipc + + Send messages between two cores using mbox and shared data structure. + +Overview +******** + +This application demonstrates how to communcate between cores without any backend in +Zephyr. It is designed to demonstrate how to integrate it with Zephyr both +from a build perspective and code. + +Building the application for nrf54l15pdk/nrf54l15/cpuapp +***************************************************** + +.. zephyr-app-commands:: + :zephyr-app: samples/subsys/ipc/basic + :board: nrf54l15pdk/nrf54l15/cpuapp + :goals: debug + :west-args: --sysbuild + +Open a serial terminal (minicom, putty, etc.) and connect the board with the +following settings: + +- Speed: 115200 +- Data: 8 bits +- Parity: None +- Stop bits: 1 + +Reset the board and the following message will appear on the corresponding +serial port, one is host another is remote: + +.. code-block:: console + + *** Booting Zephyr OS build v3.7.0-rc1-427-g7f891a6a40e8 *** + I: No data from remote + I: Sent 14 bytes to host + I: Read 14 bytes from remote, received 14 + I: Basic core-communication HOST demo ended + +.. code-block:: console + + *** Booting Zephyr OS build v3.7.0-rc1-427-g7f891a6a40e8 *** + I: No data from host + I: Read 14 bytes from host, received 14 + I: Sent 14 bytes to host + I: Basic core-communication REMOTE demo ended diff --git a/samples/subsys/ipc/basic/boards/nrf54l15pdk_nrf54l15_cpuapp.overlay b/samples/subsys/ipc/basic/boards/nrf54l15pdk_nrf54l15_cpuapp.overlay new file mode 100644 index 0000000000000..d050fbea40d78 --- /dev/null +++ b/samples/subsys/ipc/basic/boards/nrf54l15pdk_nrf54l15_cpuapp.overlay @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + soc { + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + + sram_rx: memory@20018000 { + reg = <0x20018000 0x0800>; + }; + + sram_tx: memory@20020000 { + reg = <0x20020000 0x0800>; + }; + }; + }; + + mbox_consumer { + compatible = "vnd,mbox-consumer"; + mboxes = <&cpuapp_vevif_rx 15>, <&cpuapp_vevif_tx 16>; + mbox-names = "rx", "tx"; + }; +}; + +&cpuapp_vevif_rx { + status = "okay"; +}; + +&cpuapp_vevif_tx { + status = "okay"; +}; diff --git a/samples/subsys/ipc/basic/common/common.h b/samples/subsys/ipc/basic/common/common.h new file mode 100644 index 0000000000000..4a56f7e5bb215 --- /dev/null +++ b/samples/subsys/ipc/basic/common/common.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __COMMON_H__ +#define __COMMON_H__ + +#include + +#define PACKET_SIZE_START (40) +#define DATA_SIZE (128) +#define SENDING_TIME_MS (1000) + +typedef struct __attribute__((packed)) { + uint8_t some; + uint16_t data; + uint32_t to; + uint32_t send; + uint16_t trough; + uint8_t ipc; +} data_packet_t; + +typedef struct { + atomic_bool lock; + unsigned long size; + unsigned char buffer[DATA_SIZE]; +} shared_t; + +#endif /* __COMMON_H__ */ diff --git a/samples/subsys/ipc/basic/prj.conf b/samples/subsys/ipc/basic/prj.conf new file mode 100644 index 0000000000000..c091e8b210345 --- /dev/null +++ b/samples/subsys/ipc/basic/prj.conf @@ -0,0 +1,4 @@ +CONFIG_MBOX=y + +CONFIG_LOG=y +CONFIG_LOG_MODE_MINIMAL=y diff --git a/samples/subsys/ipc/basic/remote/CMakeLists.txt b/samples/subsys/ipc/basic/remote/CMakeLists.txt new file mode 100644 index 0000000000000..bf50ebd2647ed --- /dev/null +++ b/samples/subsys/ipc/basic/remote/CMakeLists.txt @@ -0,0 +1,14 @@ +# +# Copyright (c) 2022 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 +# + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(ipc_basic_remote) + +target_include_directories(app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../common) + +target_sources(app PRIVATE src/main.c) diff --git a/samples/subsys/ipc/basic/remote/boards/nrf54l15pdk_nrf54l15_cpuflpr.overlay b/samples/subsys/ipc/basic/remote/boards/nrf54l15pdk_nrf54l15_cpuflpr.overlay new file mode 100644 index 0000000000000..50e74cf7b2b6c --- /dev/null +++ b/samples/subsys/ipc/basic/remote/boards/nrf54l15pdk_nrf54l15_cpuflpr.overlay @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + soc { + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + + sram_tx: memory@20018000 { + reg = <0x20018000 0x0800>; + }; + + sram_rx: memory@20020000 { + reg = <0x20020000 0x0800>; + }; + }; + }; + + mbox_consumer { + compatible = "vnd,mbox-consumer"; + mboxes = <&cpuflpr_vevif_rx 16>, <&cpuflpr_vevif_tx 15>; + mbox-names = "rx", "tx"; + }; +}; + +&cpuflpr_vevif_rx { + status = "okay"; +}; + +&cpuflpr_vevif_tx { + status = "okay"; +}; diff --git a/samples/subsys/ipc/basic/remote/prj.conf b/samples/subsys/ipc/basic/remote/prj.conf new file mode 100644 index 0000000000000..f2d0e67dea5c9 --- /dev/null +++ b/samples/subsys/ipc/basic/remote/prj.conf @@ -0,0 +1,6 @@ +CONFIG_MBOX=y + +CONFIG_LOG=y +CONFIG_LOG_MODE_MINIMAL=y + +CONFIG_MULTITHREADING=n diff --git a/samples/subsys/ipc/basic/remote/src/main.c b/samples/subsys/ipc/basic/remote/src/main.c new file mode 100644 index 0000000000000..ef4934babdeb7 --- /dev/null +++ b/samples/subsys/ipc/basic/remote/src/main.c @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include +#include + +#include "common.h" + +#include +LOG_MODULE_REGISTER(host, LOG_LEVEL_INF); + +static const struct mbox_dt_spec rx_channel = MBOX_DT_SPEC_GET(DT_PATH(mbox_consumer), rx); +static const struct mbox_dt_spec tx_channel = MBOX_DT_SPEC_GET(DT_PATH(mbox_consumer), tx); +static shared_t *rx_data = (shared_t *)((uint8_t *)(DT_REG_ADDR(DT_NODELABEL(sram_rx)))); +static shared_t *tx_data = (shared_t *)((uint8_t *)(DT_REG_ADDR(DT_NODELABEL(sram_tx)))); + +data_packet_t data_packet; + +/** + * @brief Send data to aother core + * @param buffer + * @param size + * @return -1 if lock is not acquired, otherwise how many items were transfered + */ +int send_to_host(const unsigned char *const restrict buffer, const unsigned int size) +{ + /* Try and get lock */ + if (atomic_flag_test_and_set(&tx_data->lock)) { + /* Return -1 in case lock is not acquired (used by other core)*/ + return -1; + } + + /* Limit size of buffer to be transfered to the size of destine buffer */ + tx_data->size = (size <= DATA_SIZE) ? size : DATA_SIZE; + + /* Copy from buffer[] to tx_data->buffer[] */ + for (unsigned int i = 0; i < tx_data->size; i++) { + tx_data->buffer[i] = buffer[i]; + } + + /* Clear lock */ + atomic_flag_clear(&tx_data->lock); + + if (mbox_send_dt(&tx_channel, NULL)) { + /* Return -2 in case when signal could not be sent by mbox driver */ + return -2; + } + + /* Return how many items were transfered */ + return tx_data->size; +} + +/** + * @brief Get data from another core + * @param buffer + * @param size + * @return -1 if lock is not acquired, otherwise how many items were read + */ +int read_from_host(unsigned char *const restrict buffer, unsigned int size) +{ + /* Try and get lock */ + if (atomic_flag_test_and_set(&rx_data->lock)) { + /* Return -1 in case lock is not acquired (used by other core)*/ + return -1; + } + + /* Verify whether we are trying to read more items than are available */ + if (size >= rx_data->size) { + size = rx_data->size; + } + + /* Copy items from rx_data->buffer[] to buffer[] */ + for (unsigned int i = 0; i < size; i++) { + buffer[i] = rx_data->buffer[i]; + } + + /* Clear shared_data.buffer_size (there is no more data available) */ + rx_data->size = 0; + + /* Clear lock */ + atomic_flag_clear(&rx_data->lock); + + /* Return how many items were read */ + return size; +} + +/** + * @brief Verify whether another core has data ready for us to read + * @return -1 if lock is not acquired, otherwise how many items are available for reading + */ +int host_has_data(void) +{ + int n_items; + + /* Try and get lock */ + if (atomic_flag_test_and_set(&rx_data->lock)) { + /* Return -1 in case lock is not acquired (used by other core)*/ + return -1; + } + + n_items = (int)rx_data->size; + + /* Clear lock */ + atomic_flag_clear(&rx_data->lock); + + return n_items; +} + +static void mbox_callback(const struct device *instance, uint32_t channel, void *user_data, + struct mbox_msg *msg_data) +{ + (void)user_data; + (void)msg_data; + + int bytes_all = host_has_data(); + + if (bytes_all > 0) { + int bytes_read = read_from_host((unsigned char *const restrict)&data_packet, + sizeof(data_packet)); + if (bytes_read < 0) { + LOG_ERR("Failed to read from host"); + } else { + LOG_INF("Read %d bytes from host, received %d", bytes_read, bytes_all); + } + } else { + LOG_INF("No data from host"); + } +} + +static int mbox_init(void) +{ + int ret; + + ret = mbox_register_callback_dt(&rx_channel, mbox_callback, NULL); + if (ret < 0) { + LOG_ERR("Could not register callback (%d)", ret); + return ret; + } + + ret = mbox_set_enabled_dt(&rx_channel, true); + if (ret < 0) { + LOG_ERR("Could not enable RX channel %d (%d)", rx_channel.channel_id, ret); + return ret; + } + + return 0; +} + +int main(void) +{ + int bytes_sent = 0; + + int ret = mbox_init(); + if (ret < 0) { + LOG_ERR("Could not init mbox (%d)", ret); + return 0; + } + + /* clear the buffer locks and their size holders */ + atomic_flag_clear(&rx_data->lock); + rx_data->size = 0; + + /* Send signal only to other core */ + ret = mbox_send_dt(&tx_channel, NULL); + if (ret < 0) { + printk("Could not send (%d)\n", ret); + return 0; + } + + k_busy_wait(100000); + + /* Send struct with data to other core */ + while (bytes_sent <= 0) { + bytes_sent = send_to_host((const unsigned char *const restrict)&data_packet, + sizeof(data_packet)); + if (bytes_sent < 0) { + LOG_ERR("Failed to send to host: %d", bytes_sent); + } else { + LOG_INF("Sent %d bytes to host", bytes_sent); + } + } + + k_busy_wait(100000); + + LOG_INF("Basic core-communication REMOTE demo ended"); + + return 0; +} diff --git a/samples/subsys/ipc/basic/sample.yaml b/samples/subsys/ipc/basic/sample.yaml new file mode 100644 index 0000000000000..4dc293ad2328c --- /dev/null +++ b/samples/subsys/ipc/basic/sample.yaml @@ -0,0 +1,20 @@ +sample: + name: Basic cores communication (structure) +tests: + sample.cores_communication: + platform_allow: nrf54l15pdk/nrf54l15/cpuapp + integration_platforms: + - nrf54l15pdk/nrf54l15/cpuapp + tags: ipc + extra_args: + basic_SNIPPET=nordic-flpr + sysbuild: true + harness: console + harness_config: + type: multi_line + ordered: false + regex: + - "No data from remote" + - "Sent 14 bytes to host" + - "Read 14 bytes from remote, received 14" + - "Basic core-communication HOST demo ended" diff --git a/samples/subsys/ipc/basic/src/main.c b/samples/subsys/ipc/basic/src/main.c new file mode 100644 index 0000000000000..46905ccfe2c64 --- /dev/null +++ b/samples/subsys/ipc/basic/src/main.c @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include +#include + +#include "common.h" + +#include +LOG_MODULE_REGISTER(host, LOG_LEVEL_INF); + +static const struct mbox_dt_spec rx_channel = MBOX_DT_SPEC_GET(DT_PATH(mbox_consumer), rx); +static const struct mbox_dt_spec tx_channel = MBOX_DT_SPEC_GET(DT_PATH(mbox_consumer), tx); +static shared_t *rx_data = (shared_t *)((uint8_t *)(DT_REG_ADDR(DT_NODELABEL(sram_rx)))); +static shared_t *tx_data = (shared_t *)((uint8_t *)(DT_REG_ADDR(DT_NODELABEL(sram_tx)))); + +data_packet_t data_packet; + +/** + * @brief Send data to aother core + * @param buffer + * @param size + * @return -1 if lock is not acquired, otherwise how many items were transfered + */ +int send_to_remote(const unsigned char *const restrict buffer, const unsigned int size) +{ + /* Try and get lock */ + if (atomic_flag_test_and_set(&tx_data->lock)) { + /* Return -1 in case lock is not acquired (used by other core)*/ + return -1; + } + + /* Limit size of buffer to be transfered to the size of destine buffer */ + tx_data->size = (size <= DATA_SIZE) ? size : DATA_SIZE; + + /* Copy from buffer[] to tx_data->buffer[] */ + for (unsigned int i = 0; i < tx_data->size; i++) { + tx_data->buffer[i] = buffer[i]; + } + + /* Clear lock */ + atomic_flag_clear(&tx_data->lock); + + if (mbox_send_dt(&tx_channel, NULL)) { + /* Return -2 in case when signal could not be sent by mbox driver */ + return -2; + } + + /* Return how many items were transfered */ + return tx_data->size; +} + +/** + * @brief Get data from another core + * @param buffer + * @param size + * @return -1 if lock is not acquired, otherwise how many items were read + */ +int read_from_remote(unsigned char *const restrict buffer, unsigned int size) +{ + /* Try and get lock */ + if (atomic_flag_test_and_set(&rx_data->lock)) { + /* Return -1 in case lock is not acquired (used by other core)*/ + return -1; + } + + /* Verify whether we are trying to read more items than are available */ + if (size >= rx_data->size) { + size = rx_data->size; + } + + /* Copy items from rx_data->buffer[] to buffer[] */ + for (unsigned int i = 0; i < size; i++) { + buffer[i] = rx_data->buffer[i]; + } + + /* Clear shared_data.buffer_size (there is no more data available) */ + rx_data->size = 0; + + /* Clear lock */ + atomic_flag_clear(&rx_data->lock); + + /* Return how many items were read */ + return size; +} + +/** + * @brief Verify whether another core has data ready for us to read + * @return -1 if lock is not acquired, otherwise how many items are available for reading + */ +int remote_has_data(void) +{ + int n_items; + + /* Try and get lock */ + if (atomic_flag_test_and_set(&rx_data->lock)) { + /* Return -1 in case lock is not acquired (used by other core)*/ + return -1; + } + + n_items = (int)rx_data->size; + + /* Clear lock */ + atomic_flag_clear(&rx_data->lock); + + return n_items; +} + +static void mbox_callback(const struct device *instance, uint32_t channel, void *user_data, + struct mbox_msg *msg_data) +{ + (void)user_data; + (void)msg_data; + + int bytes_all = remote_has_data(); + + if (bytes_all > 0) { + int bytes_read = read_from_remote((unsigned char *const restrict)&data_packet, + sizeof(data_packet)); + if (bytes_read < 0) { + LOG_ERR("Failed to read from remote"); + } else { + LOG_INF("Read %d bytes from remote, received %d", bytes_read, bytes_all); + } + } else { + LOG_INF("No data from remote"); + } +} + +static int mbox_init(void) +{ + int ret; + + ret = mbox_register_callback_dt(&rx_channel, mbox_callback, NULL); + if (ret < 0) { + LOG_ERR("Could not register callback (%d)", ret); + return ret; + } + + ret = mbox_set_enabled_dt(&rx_channel, true); + if (ret < 0) { + LOG_ERR("Could not enable RX channel %d (%d)", rx_channel.channel_id, ret); + return ret; + } + + return 0; +} + +int main(void) +{ + int bytes_sent = 0; + + int ret = mbox_init(); + if (ret < 0) { + LOG_ERR("Could not init mbox (%d)", ret); + return 0; + } + + /* clear the buffer locks and their size holders */ + atomic_flag_clear(&rx_data->lock); + rx_data->size = 0; + + /* Send signal to other core */ + ret = mbox_send_dt(&tx_channel, NULL); + if (ret < 0) { + printk("Could not send (%d)\n", ret); + return 0; + } + + k_busy_wait(100000); + + /* Send message to other core */ + while (bytes_sent <= 0) { + bytes_sent = send_to_remote((const unsigned char *const restrict)&data_packet, + sizeof(data_packet)); + if (bytes_sent < 0) { + LOG_ERR("Failed to send to host: %d", bytes_sent); + } else { + LOG_INF("Sent %d bytes to host", bytes_sent); + } + } + + k_busy_wait(100000); + + LOG_INF("Basic core-communication HOST demo ended"); + + return 0; +} diff --git a/samples/subsys/ipc/basic/sysbuild.cmake b/samples/subsys/ipc/basic/sysbuild.cmake new file mode 100644 index 0000000000000..62c9e931d60f1 --- /dev/null +++ b/samples/subsys/ipc/basic/sysbuild.cmake @@ -0,0 +1,14 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +if ("${SB_CONFIG_REMOTE_BOARD}" STREQUAL "") + message(FATAL_ERROR + "Target ${BOARD} not supported for this sample. " + "There is no remote board selected in Kconfig.sysbuild") +endif() + +ExternalZephyrProject_Add( + APPLICATION remote + SOURCE_DIR ${APP_DIR}/remote + BOARD ${SB_CONFIG_REMOTE_BOARD} +)