From 8e041757a60f284e4e09b637b48ba1b79af76f28 Mon Sep 17 00:00:00 2001 From: Dominik Chat Date: Wed, 21 Jul 2021 15:42:07 +0200 Subject: [PATCH] sm_ipt: Add Shared Memory Inter-Processor Transport SM_IPT is OS independent shared memory communication protocol, which works on shared memory and provides os-porting header templates. Signed-off-by: Dominik Chat --- CMakeLists.txt | 3 +- CODEOWNERS | 1 + Kconfig.nrfxlib | 3 +- README.rst | 1 + sm_ipt/CMakeLists.txt | 11 + sm_ipt/Kconfig | 42 +++ sm_ipt/README.rst | 16 ++ sm_ipt/doc/api.rst | 17 ++ sm_ipt/doc/img/architecture.svg | 3 + sm_ipt/doc/usage.rst | 219 ++++++++++++++++ sm_ipt/include/sm_ipt.h | 122 +++++++++ sm_ipt/include/sm_ipt_errno.h | 64 +++++ sm_ipt/sm_ipt.c | 420 ++++++++++++++++++++++++++++++ sm_ipt/template/sm_ipt_log_tmpl.h | 70 +++++ sm_ipt/template/sm_ipt_os_tmpl.h | 114 ++++++++ 15 files changed, 1104 insertions(+), 2 deletions(-) create mode 100644 sm_ipt/CMakeLists.txt create mode 100644 sm_ipt/Kconfig create mode 100644 sm_ipt/README.rst create mode 100644 sm_ipt/doc/api.rst create mode 100644 sm_ipt/doc/img/architecture.svg create mode 100644 sm_ipt/doc/usage.rst create mode 100644 sm_ipt/include/sm_ipt.h create mode 100644 sm_ipt/include/sm_ipt_errno.h create mode 100644 sm_ipt/sm_ipt.c create mode 100644 sm_ipt/template/sm_ipt_log_tmpl.h create mode 100644 sm_ipt/template/sm_ipt_os_tmpl.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 6fed0d9346..e83c247579 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright (c) 2019 - 2020 Nordic Semiconductor +# Copyright (c) 2019 - 2021 Nordic Semiconductor # # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause # @@ -19,3 +19,4 @@ add_subdirectory_ifdef(CONFIG_BT softdevice_controller) add_subdirectory_ifdef(CONFIG_NET_L2_OPENTHREAD openthread) add_subdirectory_ifdef(CONFIG_NRF_RPC nrf_rpc) add_subdirectory_ifdef(CONFIG_ZIGBEE zboss) +add_subdirectory_ifdef(CONFIG_SM_IPT sm_ipt) diff --git a/CODEOWNERS b/CODEOWNERS index 6528ba612f..4a87ef15de 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -34,3 +34,4 @@ doc/* @b-gent /zboss/ @tomchy /zephyr/ @carlescufi /nrf_rpc/ @doki-nordic @KAGA164 +/sm_ipt/ @doki-nordic diff --git a/Kconfig.nrfxlib b/Kconfig.nrfxlib index 753e125614..7c70e71737 100644 --- a/Kconfig.nrfxlib +++ b/Kconfig.nrfxlib @@ -1,5 +1,5 @@ # -# Copyright (c) 2019 - 2020 Nordic Semiconductor +# Copyright (c) 2019 - 2021 Nordic Semiconductor # # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause # @@ -13,6 +13,7 @@ rsource "crypto/Kconfig" rsource "nrf_security/Kconfig" rsource "softdevice_controller/Kconfig" rsource "nrf_rpc/Kconfig" +rsource "sm_ipt/Kconfig" rsource "zboss/Kconfig" rsource "openthread/Kconfig" rsource "nrf_802154/zephyr/Kconfig.nrfxlib" diff --git a/README.rst b/README.rst index 51422c38d4..6110876caf 100644 --- a/README.rst +++ b/README.rst @@ -23,3 +23,4 @@ Refer to their respective documentation for more information. nrf_rpc/README softdevice_controller/README zboss/README + sm_ipt/README diff --git a/sm_ipt/CMakeLists.txt b/sm_ipt/CMakeLists.txt new file mode 100644 index 0000000000..6b14d6e943 --- /dev/null +++ b/sm_ipt/CMakeLists.txt @@ -0,0 +1,11 @@ +# +# Copyright (c) 2021 Nordic Semiconductor +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +zephyr_include_directories(include) + +zephyr_library() + +zephyr_library_sources_ifdef(CONFIG_SM_IPT sm_ipt.c) diff --git a/sm_ipt/Kconfig b/sm_ipt/Kconfig new file mode 100644 index 0000000000..2f4292ffea --- /dev/null +++ b/sm_ipt/Kconfig @@ -0,0 +1,42 @@ +# +# Copyright (c) 2021 Nordic Semiconductor +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +menuconfig SM_IPT + bool "Shared memory Inter-Processor Transport" + help + Enable Shared memory Inter-Processor Transport library + +if SM_IPT + +choice + prompt "Shared memory blocks" + default SM_IPT_NUM_BLOCKS_32 + +config SM_IPT_NUM_BLOCKS_32 + bool "32 shared memory blocks per channel" + help + Selects 32 shared memory blocks per single input or output channel. + +config SM_IPT_NUM_BLOCKS_64 + bool "64 shared memory blocks per channel" + help + Selects 64 shared memory blocks per single input or output channel. + This will increase allocatable memory granularity, but it will have + some impact on performance and code size. + +endchoice + +config SM_IPT_PRIMARY + bool "Primary role" + help + One side must have primary role (this option selected) and the other + side must have secondary role. Shared memory layout depends on this configuration. + +module = SM_IPT +module-str = SM IPT +source "${ZEPHYR_BASE}/subsys/logging/Kconfig.template.log_config" + +endif # SM_IPT diff --git a/sm_ipt/README.rst b/sm_ipt/README.rst new file mode 100644 index 0000000000..9870f93ad4 --- /dev/null +++ b/sm_ipt/README.rst @@ -0,0 +1,16 @@ +.. _sm_ipt: + +Shared Memory Inter-Processor Transport library (SM IPT) +####################################### + +SM IPT is a inter-processor transport library for |NCS| enabling inter-processor communication on Nordic Semiconductor SoCs. +The library is RTOS-agnostic and provides porting templates. + +It is designed to be used with as a transport layer, or standalone. + +.. toctree:: + :maxdepth: 2 + :caption: Subpages: + + doc/usage + doc/api diff --git a/sm_ipt/doc/api.rst b/sm_ipt/doc/api.rst new file mode 100644 index 0000000000..77e7b9a6fc --- /dev/null +++ b/sm_ipt/doc/api.rst @@ -0,0 +1,17 @@ +.. _sm_ipt_api: + +API documentation +################# + +.. contents:: + :local: + :depth: 2 + +.. _sm_ipt_api_documentation: + +SM IPT API documentation +---------------------- + +.. doxygengroup:: sm_ipt + :project: nrfxlib + :members: diff --git a/sm_ipt/doc/img/architecture.svg b/sm_ipt/doc/img/architecture.svg new file mode 100644 index 0000000000..e0efb6c414 --- /dev/null +++ b/sm_ipt/doc/img/architecture.svg @@ -0,0 +1,3 @@ + + +

User API
User API
User API
User API
User API
User API
User API
User API
SM IPT
sm_ipt.h
SM IPT...
OS abstraction
sm_ipt_os_tmpl.h
OS abstraction...
Logger abstraction
sm_ipt_log_tmpl.h
Logger abstraction...
Shared Memory
Shared Memory
OS
OS
User API
User API
SM IPT OS-independent layer
Creating packets
Managing memory
SM IPT OS-independent layer...
SM IPT OS-dependent layer
Cooperating with OS
Managing callbacks
Logging support
Acquiring shared memory
SM IPT OS-dependent layer...
OS and HW layer
OS and HW layer
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/sm_ipt/doc/usage.rst b/sm_ipt/doc/usage.rst new file mode 100644 index 0000000000..6e725efe85 --- /dev/null +++ b/sm_ipt/doc/usage.rst @@ -0,0 +1,219 @@ +.. _sm_ipt_usage: + +Usage and Implementation +######################## + +.. contents:: + :local: + :depth: 2 + +The following picture gives an overview of the SM IPT architecture: + +.. figure:: img/architecture.svg + :alt: SM IPT architecture layers + :align: center + + SM IPT architecture layers + +Usage +===== + +.. note:: + Following examples are purely conceptual and simplified to show library functions. They should be rewritten to suit particular need and use OS modules like FIFO. + + +Initialization +-------------- + +The initialization function initializes context data and retrieves shared memory parameters from ``sm_ipt_os_init`` function. + +.. code-block:: c + + struct sm_ipt_ctx sm_ipt_ctx; + + void sm_ipt_receive_handler(const uint8_t *packet, size_t len) + { + /* Process data */ + some_data_processing_funtion(); + } + + int initialize(void) + { + return sm_ipt_init(&sm_ipt_ctx, sm_ipt_receive_handler); + } + +The above code uses the ``sm_ipt_init`` function to initialize ``struct sm_ipt_ctx`` SM IPT context data structure and assign the receive handler. + +Data send full-copy +------------------- + +The data is prepared beforehand, copied into allocated buffer and send to another core. + +.. code-block:: c + + int send(const void *data, size_t len) + { + uint8_t *buffer; + + sm_ipt_alloc_tx_buf(&sm_ipt_ctx, &buffer, len); + + memcpy(buffer, data, len); + + return sm_ipt_send(&sm_ipt_ctx, buffer, len); + } + +The above code uses the ``sm_ipt_alloc_tx_buf`` function to allocate transmit buffer in shared memory. Data is copied into said buffer and sent to another core via ``sm_ipt_send`` function. + +Data send no-copy +------------------- + +The data is prepared after buffer allocation send to another core when data preparation (and usage) is finished. + +.. code-block:: c + + int send_nocopy(size_t len) + { + uint8_t *buffer; + size_t i; + + sm_ipt_alloc_tx_buf(&sm_ipt_ctx, &buffer, len); + + /* Use buffer as any other allocated buffer */ + some_data_preparation_funtion(buffer); + + return sm_ipt_send(&sm_ipt_ctx, buffer, len); + } + +The above code uses the ``sm_ipt_alloc_tx_buf`` function to allocate transmit buffer in shared memory. Buffer is prepared by ``some_data_preparation_funtion`` and sent to another core via ``sm_ipt_send`` function. + +Data send abort +------------------- + +There is possibility to free transmit buffer instead of sending it if the need arises. + +.. code-block:: c + + int send_if(size_t len, bool send_choice) + { + uint8_t *buffer; + size_t i; + + sm_ipt_alloc_tx_buf(&sm_ipt_ctx, &buffer, len); + + if (send_choice){ + /* Prepare some data */ + some_data_preparation_funtion(buffer); + + return sm_ipt_send(&sm_ipt_ctx, buffer, len); + } + else{ + sm_ipt_free_tx_buf(&sm_ipt_ctx, buffer); + + return ERROR_ABORT; + } + } + +The above code uses the ``sm_ipt_alloc_tx_buf`` function to allocate transmit buffer in shared memory. Buffer is send only if send_choice is true. If send_choice is false the buffer is free using ``sm_ipt_free_tx_buf`` and send operation is aborted. + + +Data receive full-copy +------------------- + +When data is received it can be processed using full-copy mechanism. Buffer has to be copied to local memory. Shared memory rx buffer has to be freed and data processing is done on local copy of buffer. + +.. code-block:: c + + volatile uint8_t receive_buffer[MAX_PACKET_SIZE]; + volatile bool data_received; + + void sm_ipt_receive_handler(const uint8_t *packet, size_t len) + { + /* Copy whole message */ + memcpy(receive_buffer, packet, len); + + /* Free receive buffer */ + sm_ipt_free_rx_buf(&sm_ipt_ctx, packet); + + /* Signal message received */ + data_received = true; + } + + void data_processing_thread(void) + { + /* Wait for incoming data */ + while(!data_received); + + /*Process data */ + some_data_processing_funtion(receive_buffer); + } + +The above code uses the ``memcpy`` function to copy shared memory buffer into local buffer. Next shared memory buffer is freed using ``sm_ipt_free_rx_buf``. Lastly the data is processed locally using a copy of buffer. + + +Data receive no-copy +------------------- + +When data is received it can be processed using no-copy mechanism. The pointer to shared memory buffer has to be saved in order to use it later. Shared memory rx buffer has to be freed and data processing is done on shared memory buffer. + +.. note:: + This approach increases risk of out of memory errors due to not freeing buffer right after receiving data. + +.. code-block:: c + + volatile void *receive_buffer; + volatile bool data_received; + + void sm_ipt_receive_handler(const uint8_t *packet, size_t len) + { + /* Save buffer pointer */ + receive_buffer = (void *) packet; + + /* Signal message received */ + data_received = true; + } + + void data_processing_thread(void) + { + /* Wait for incoming data */ + while(!data_received); + + /*Process data */ + some_data_processing_funtion(receive_buffer); + + /* Free receive buffer */ + sm_ipt_free_rx_buf(&sm_ipt_ctx, packet); + } + +The above code copies pointer to shared memory buffer. Next data is processed using original buffer in share memory. Lastly shared memory buffer is freed using ``sm_ipt_free_rx_buf``. + + +Lower layers +============ + +The lower layers of SM IPT are OS-dependent. +They are responsible for memory managment, synchronization and logging support. + +.. note:: + Detailed knowledge about how the lower layers are implemented is not required when using the SM IPT API. + However, this knowledge is required to port to a different operating system. + +Operating system abstraction +---------------------------- + +The operating system abstraction provides functionality for SM IPT that depends on the operating system. +It manages the shared memory, thread synchronization, and atomic support. + +The template header describing the OS abstraction is :file:`template/sm_ipt_os_tmpl.h`. + + +Logging +------- + +SM IPT logs some of its activities. +This allows for tracking, diagnosis, and debugging. +It provides four levels for logging: errors, warnings, information, and debug. + +Error logs indicate serious errors, so they should be enabled if possible. +Debug logs should be enabled only to track specific problems. + +The template header describing the logger is :file:`template/sm_ipt_log_tmpl.h`. diff --git a/sm_ipt/include/sm_ipt.h b/sm_ipt/include/sm_ipt.h new file mode 100644 index 0000000000..42fc4103ac --- /dev/null +++ b/sm_ipt/include/sm_ipt.h @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2021 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic + */ + +#ifndef SM_IPT_H_ +#define SM_IPT_H_ + +#include +#include +#include +#include "sm_ipt_os.h" + +/** + * @defgroup sm_ipt SM IPT (Shared Memory Inter-Processor Transport) module. + * @{ + * @brief Module to facilitate communication using shared memory. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Callback to data receiving function. + * + * This callback will be called when packet is received. + * + * @param packet Received data. + * @param len Length of received data. + */ +typedef void (*sm_ipt_receive_handler_t)(const uint8_t *packet, size_t len); + +/** @brief Single shared memory queue. + * + * Used by @ref sm_ipt_ctx to store queue information. + */ +struct sm_ipt_queue { + size_t allocable_size; + size_t queue_items; + size_t block_size; + uint8_t *allocable; + uint32_t *queue_tx; + uint32_t *queue_rx; + uint8_t *queue; + uint8_t *handshake; +}; + +/** @brief SM IPT context. + * + * Used to store current context data. + */ +struct sm_ipt_ctx { + struct sm_ipt_queue out; + struct sm_ipt_queue in; + sm_ipt_os_sem_t out_sem; + sm_ipt_os_mutex_t out_mutex; + sm_ipt_os_atomic_t free_mask[IS_ENABLED(CONFIG_SM_IPT_NUM_BLOCKS_64) ? 2 : 1]; + sm_ipt_receive_handler_t receive_handler; + struct sm_ipt_os_ctx os_ctx; +}; + +/** @brief Initialize the SM IPT + * + * This function initializes shared memory and context data. + * + * @param ctx SM ITP Context. + * @param callback Callback to receive handler. + * + * @return 0 on success or negative error code. + */ +int sm_ipt_init(struct sm_ipt_ctx *ctx, sm_ipt_receive_handler_t callback); + +/** @brief Free SM IPT rx buffer + * + * This function frees shared memory buffer for receiving data. + * + * @param ctx SM ITP Context. + * @param packet Selected buffer. + */ +void sm_ipt_free_rx_buf(struct sm_ipt_ctx *ctx, const uint8_t *packet); + +/** @brief Allocate SM IPT tx buffer + * + * This function allocates shared memory buffer for trasmitting data. + * + * @param ctx SM ITP Context. + * @param packet Pointer to selected buffer. + * @param len Length of allocation. + */ +void sm_ipt_alloc_tx_buf(struct sm_ipt_ctx *ctx, uint8_t **buf, size_t len); + +/** @brief Free SM IPT tx buffer + * + * This function frees shared memory buffer for trasmitting data. + * + * @param ctx SM ITP Context. + * @param packet Pointer to selected buffer. + */ +void sm_ipt_free_tx_buf(struct sm_ipt_ctx *ctx, uint8_t *buf); + +/** @brief Send message through SM IPT + * + * This function sends data stored at buf in shared memory. + * + * @param ctx SM ITP Context. + * @param packet Selected buffer. + * @param len Length of message. + * + * @return 0 on success or negative error code. + */ +int sm_ipt_send(struct sm_ipt_ctx *ctx, uint8_t *buf, size_t len); + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* SM_IPT_H_ */ diff --git a/sm_ipt/include/sm_ipt_errno.h b/sm_ipt/include/sm_ipt_errno.h new file mode 100644 index 0000000000..d03904ed34 --- /dev/null +++ b/sm_ipt/include/sm_ipt_errno.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2021 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#ifndef NRF_ERRNO_H__ +#define NRF_ERRNO_H__ + +/** + * @defgroup nrf_rpc_errno Error codes for SM IPT. + * @{ + * @ingroup sm_ipt + * + * @brief Defies error codes that can be used in SM IPT. + */ + +#define NRF_EPERM 1 /**< Operation not permitted */ +#define NRF_ENOENT 2 /**< No such file or directory */ +#define NRF_EIO 5 /**< Input/output error */ +#define NRF_ENOEXEC 8 /**< Exec format error */ +#define NRF_EBADF 9 /**< Bad file descriptor */ +#define NRF_ENOMEM 12 /**< Cannot allocate memory */ +#define NRF_EACCES 13 /**< Permission denied */ +#define NRF_EFAULT 14 /**< Bad address */ +#define NRF_ENODEV 19 /**< No such device */ +#define NRF_EINVAL 22 /**< Invalid argument */ +#define NRF_EMFILE 24 /**< Too many open files */ +#define NRF_ENOSPC 28 /**< No space left on device */ +#define NRF_EAGAIN 35 /**< Resource temporarily unavailable*/ +#define NRF_EDOM 37 /**< Domain error */ +#define NRF_EMSGSIZE 40 /**< Message too long */ +#define NRF_EPROTOTYPE 41 /**< Protocol wrong type for socket */ +#define NRF_ENOPROTOOPT 42 /**< Protocol not available */ +#define NRF_EPROTONOSUPPORT 43 /**< Protocol not supported */ +#define NRF_ESOCKTNOSUPPORT 44 /**< Socket type not supported */ +#define NRF_EOPNOTSUPP 45 /**< Operation not supported */ +#define NRF_EAFNOSUPPORT 47 /**< Address family not supported by protocol */ +#define NRF_EADDRINUSE 48 /**< Address already in use */ +#define NRF_ENETDOWN 50 /**< Network is down */ +#define NRF_ENETUNREACH 51 /**< Network is unreachable */ +#define NRF_ENETRESET 52 /**< Connection aborted by network */ +#define NRF_ECONNRESET 54 /**< Connection reset by peer */ +#define NRF_EISCONN 56 /**< Transport endpoint is already connected */ +#define NRF_ENOTCONN 57 /**< Transport endpoint is not connected */ +#define NRF_ETIMEDOUT 60 /**< Connection timed out */ +#define NRF_EBADMSG 77 /**< Bad message */ +#define NRF_ENOBUFS 105 /**< No buffer space available */ + +#define NRF_EHOSTDOWN 112 /**< Host is down */ +#define NRF_EALREADY 114 /**< Operation already in progress */ +#define NRF_EINPROGRESS 115 /**< Operation in progress */ +#define NRF_ECANCELED 125 /**< Operation canceled */ + +#define NRF_ENOKEY 126 /**< Required key not available */ +#define NRF_EKEYEXPIRED 127 /**< Key has expired */ +#define NRF_EKEYREVOKED 128 /**< Key has been revoked */ +#define NRF_EKEYREJECTED 129 /**< Key was rejected by service */ + +/** + * @} + */ + +#endif // NRF_ERRNO_H__ diff --git a/sm_ipt/sm_ipt.c b/sm_ipt/sm_ipt.c new file mode 100644 index 0000000000..28cee57340 --- /dev/null +++ b/sm_ipt/sm_ipt.c @@ -0,0 +1,420 @@ +/* + * Copyright (c) 2021 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic + */ + + +#include +#include +#include + +#include "sm_ipt.h" +#include "sm_ipt_log.h" +#include "sm_ipt_errno.h" + + +#define FLAG_RELEASE 0x80 + +#define WORD_SIZE sizeof(uint32_t) +#define WORD_BITS (8 * sizeof(uint32_t)) + +#if defined(CONFIG_SM_IPT_NUM_BLOCKS_32) +typedef uint32_t mask_t; +typedef int32_t smask_t; +#define NUM_BLOCKS 32 +#define mask_clz sm_ipt_os_clz32 +#elif defined(CONFIG_SM_IPT_NUM_BLOCKS_64) +typedef uint64_t mask_t; +typedef int64_t smask_t; +#define NUM_BLOCKS 64 +#define mask_clz sm_ipt_os_clz64 +#else +#error Number of shared dynamic memory blocks is not configured. +#endif + +#define ALLOCABLE_MULTIPLY (WORD_SIZE * NUM_BLOCKS) +#define QUEUE_INDEX_MASK (NUM_BLOCKS - 1) + +#define memory_corrupted_error() sm_ipt_os_fatal(); \ + SM_IPT_ERR("Shared memory corrupted"); \ + SM_IPT_ASSERT(0) + + +static inline void free_mask_set(struct sm_ipt_ctx *ctx, mask_t mask) +{ + sm_ipt_os_atomic_or(&ctx->free_mask[0], mask); + if (NUM_BLOCKS > 32) { + sm_ipt_os_atomic_or(&ctx->free_mask[1], (uint64_t)mask >> 32); + } + SM_IPT_OS_MEMORY_BARIER(); +} + +static bool free_mask_unset(struct sm_ipt_ctx *ctx, mask_t mask) +{ + uint32_t old; + uint32_t mask_part; + + mask_part = (uint32_t)mask; + old = sm_ipt_os_atomic_and(&ctx->free_mask[0], ~mask_part); + if ((old & mask_part) != mask_part) { + sm_ipt_os_atomic_or(&ctx->free_mask[0], old & mask_part); + return false; + } + if (NUM_BLOCKS > 32) { + mask_part = (uint32_t)((uint64_t)mask >> 32); + old = sm_ipt_os_atomic_and(&ctx->free_mask[1], ~mask_part); + if ((old & mask_part) != mask_part) { + sm_ipt_os_atomic_or(&ctx->free_mask[1], old & mask_part); + sm_ipt_os_atomic_or(&ctx->free_mask[0], (uint32_t)mask); + return false; + } + } + SM_IPT_OS_MEMORY_BARIER(); + return true; +} + +static inline mask_t free_mask_get(struct sm_ipt_ctx *ctx) +{ + mask_t mask; + + mask = sm_ipt_os_atomic_get(&ctx->free_mask[0]); + if (NUM_BLOCKS > 32) { + mask = (uint64_t)sm_ipt_os_atomic_get(&ctx->free_mask[1]) << 32; + } + + return mask; +} + +static void free_mask_init(struct sm_ipt_ctx *ctx) +{ + free_mask_set(ctx, ~(mask_t)0); +} + +static mask_t calc_mask(size_t blocks, size_t index) +{ + mask_t mask; + + mask = (mask_t)1 << (NUM_BLOCKS - 1); /* 100000000... */ + mask = (smask_t)mask >> (blocks - 1); /* 111000000... (e.g. blocks = 3) */ + mask = mask >> index; /* 000011100... (e.g. index = 4) */ + return mask; +} + +void sm_ipt_alloc_tx_buf(struct sm_ipt_ctx *ctx, uint8_t **buf, size_t len) +{ + size_t i; + /* Actual allocated memory: | 32-bit size | data | padding | */ + size_t blocks = (len + (WORD_SIZE + ctx->out.block_size - 1)) / ctx->out.block_size; + bool sem_taken = false; + mask_t cur_mask; + mask_t sh_mask; + bool unset_success; + mask_t mask; + size_t free_index; + + SM_IPT_DBG("Trying to allocate %d bytes", len); + + if (blocks > NUM_BLOCKS || blocks == 0) { + SM_IPT_ERR("Requested %d bytes, maximum is %d", len, + ctx->out.allocable_size - sizeof(uint32_t)); + *buf = NULL; + return; + } + + do { + do { + /* create shifted mask with bits set where `blocks` can be allocated */ + cur_mask = free_mask_get(ctx); + sh_mask = cur_mask; + for (i = 1; i < blocks; i++) { + sh_mask &= (sh_mask << 1); + } + + /* if no memory */ + if (sh_mask == 0) { + /* wait for any block to be empty */ + sm_ipt_os_take(&ctx->out_sem); + sem_taken = true; + } + + } while (sh_mask == 0); + + /* get first available blocks */ + free_index = mask_clz(sh_mask); + /* create bit mask with blocks that will be used */ + mask = calc_mask(blocks, free_index); + /* update masks */ + unset_success = free_mask_unset(ctx, mask); + /* there is a small probability that unset will be unsuccessful */ + if (!unset_success) { + /* give semaphore, because free_mask_unset may cause other thread + * waiting before it reverted the changes + */ + SM_IPT_DBG("Failed to allocate shared memory"); + sm_ipt_os_give(&ctx->out_sem); + sem_taken = false; + } + } while (!unset_success); + + /* Give semaphore back, because there may be some other thread waiting */ + if (sem_taken && (cur_mask & ~mask) != 0) { + sm_ipt_os_give(&ctx->out_sem); + } + + uint32_t *mem_start = (uint32_t *)&ctx->out.allocable[ctx->out.block_size * free_index]; + + mem_start[0] = blocks * ctx->out.block_size; + + *buf = (uint8_t *)(&mem_start[1]); + SM_IPT_DBG("Allocated memory at 0x%x", (uint32_t)*buf); +} + +void sm_ipt_free_tx_buf(struct sm_ipt_ctx *ctx, uint8_t *buf) +{ + uint32_t *mem_start = (uint32_t *)buf - 1; + uint32_t offset = (uint8_t *)mem_start - ctx->out.allocable; + uint32_t block_index = offset / ctx->out.block_size; + uint32_t allocated_size; + uint32_t allocated_blocks; + + SM_IPT_ASSERT(block_index < NUM_BLOCKS); + SM_IPT_ASSERT(buf == &ctx->out.allocable[block_index * ctx->out.block_size]); + + allocated_size = mem_start[0]; + allocated_blocks = allocated_size / ctx->out.block_size; + + SM_IPT_ASSERT(allocated_blocks % ctx->out.block_size == 0); + SM_IPT_ASSERT(allocated_size <= ctx->out.allocable_size); + SM_IPT_ASSERT(offset + allocated_size <= ctx->out.allocable_size); + + free_mask_set(ctx, calc_mask(allocated_blocks, block_index)); + sm_ipt_os_give(&ctx->out_sem); + SM_IPT_DBG("Dealocated TX block at 0x%x", (uint32_t)buf); +} + + +static void queue_send(struct sm_ipt_ctx *ctx, uint8_t data) +{ + uint32_t tx; + uint32_t dst; + + sm_ipt_os_lock(&ctx->out_mutex); + + tx = *ctx->out.queue_tx; + dst = tx; + + if (dst >= ctx->out.queue_items) { + memory_corrupted_error(); + return; + } + + tx++; + if (tx >= ctx->out.queue_items) { + tx = 0; + } + + ctx->out.queue[dst] = data; + SM_IPT_OS_MEMORY_BARIER(); + *ctx->out.queue_tx = tx; + + sm_ipt_os_unlock(&ctx->out_mutex); + + sm_ipt_os_signal(&ctx->os_ctx); +} + + +int sm_ipt_send(struct sm_ipt_ctx *ctx, uint8_t *buf, size_t len) +{ + uint32_t *mem_start = (uint32_t *)buf - 1; + uint32_t offset = (uint8_t *)mem_start - ctx->out.allocable; + uint32_t block_index = offset / ctx->out.block_size; + uint32_t allocated_size; + uint32_t allocated_blocks; + uint32_t blocks = (len + ctx->out.block_size - 1) / ctx->out.block_size; + uint32_t total_len = len + WORD_SIZE; + + SM_IPT_DBG("Trying to send message of: %d bytes, at address: 0x%x", len, (uint32_t) buf); + + SM_IPT_ASSERT(block_index < NUM_BLOCKS); + SM_IPT_ASSERT((uint8_t *)mem_start == + &ctx->out.allocable[block_index * ctx->out.block_size]); + + allocated_size = mem_start[0]; + allocated_blocks = allocated_size / ctx->out.block_size; + + SM_IPT_ASSERT(allocated_size % ctx->out.block_size == 0); + SM_IPT_ASSERT(allocated_size <= ctx->out.allocable_size); + SM_IPT_ASSERT(offset + allocated_size <= ctx->out.allocable_size); + SM_IPT_ASSERT(total_len <= allocated_size); + + if (blocks < allocated_blocks) { + free_mask_set(ctx, calc_mask(allocated_blocks - blocks, block_index + blocks)); + sm_ipt_os_give(&ctx->out_sem); + } + + mem_start[0] = total_len; + + queue_send(ctx, block_index); + + return 0; +} + + +static int queue_recv(struct sm_ipt_ctx *ctx) +{ + uint32_t tx = *ctx->in.queue_tx; + uint32_t rx = *ctx->in.queue_rx; + uint8_t data; + + tx = *ctx->in.queue_tx; + rx = *ctx->in.queue_rx; + + if (rx >= ctx->in.queue_items) { + memory_corrupted_error(); + return -1; + } + + if (tx == rx) { + return -1; + } + + SM_IPT_OS_MEMORY_BARIER(); + + data = ctx->in.queue[rx]; + + rx++; + if (rx >= ctx->in.queue_items) { + rx = 0; + } + + *ctx->in.queue_rx = rx; + + return data; +} + + +static void signal_received(struct sm_ipt_os_ctx *os_ctx) +{ + int block; + int release; + uint32_t *mem_start; + uint32_t total_size; + uint32_t blocks; + struct sm_ipt_ctx *ctx; + + ctx = SM_IPT_OS_GET_CONTAINTER(os_ctx, struct sm_ipt_ctx, os_ctx); + + do { + block = queue_recv(ctx); + if (block < 0) { + break; + } + + release = (block & FLAG_RELEASE); + block &= ~FLAG_RELEASE; + + if (block >= NUM_BLOCKS) { + continue; + } + + if (release) { + mem_start = (uint32_t *)(&ctx->out.allocable[block * ctx->out.block_size]); + total_size = mem_start[0]; + blocks = (total_size + ctx->out.block_size - 1) / ctx->out.block_size; + free_mask_set(ctx, calc_mask(blocks, block)); + sm_ipt_os_give(&ctx->out_sem); + } else { + mem_start = (uint32_t *)(&ctx->in.allocable[block * ctx->in.block_size]); + total_size = mem_start[0]; + if (total_size >= &ctx->in.allocable[ctx->in.allocable_size] - + (uint8_t *)mem_start || total_size < WORD_SIZE) { + memory_corrupted_error(); + break; + } + SM_IPT_DBG("Message received at: 0x%x, length: %d", (uint32_t)&mem_start[1], + (total_size - WORD_SIZE)); + ctx->receive_handler((uint8_t *)&mem_start[1], total_size - WORD_SIZE); + } + } while (true); +} + +void sm_ipt_free_rx_buf(struct sm_ipt_ctx *ctx, const uint8_t *packet) +{ + uint32_t *mem_start = (uint32_t *)packet - 1; + uint32_t offset = (uint8_t *)mem_start - ctx->in.allocable; + uint32_t block_index = offset / ctx->in.block_size; + + SM_IPT_ASSERT(block_index < NUM_BLOCKS); + + queue_send(ctx, (block_index | FLAG_RELEASE)); + SM_IPT_DBG("Dealocated RX block at 0x%x", (uint32_t)mem_start); +} + +static void handshake_step(struct sm_ipt_ctx *ctx, uint8_t this_value, uint8_t next_value) +{ + SM_IPT_DBG("Setting out handshake to: %d", this_value); + *ctx->out.handshake = this_value; + SM_IPT_OS_MEMORY_BARIER(); + SM_IPT_DBG("Waiting for handshake next value: %d", next_value); + while (*ctx->in.handshake != this_value && *ctx->in.handshake != next_value) { + sm_ipt_os_yield(); + SM_IPT_OS_MEMORY_BARIER(); + } +} + +int sm_ipt_init(struct sm_ipt_ctx *ctx, sm_ipt_receive_handler_t callback) +{ + int err; + + ctx->receive_handler = callback; + + err = sm_ipt_os_init(&ctx->os_ctx); + if (err < 0) { + return err; + } + + free_mask_init(ctx); + sm_ipt_os_sem_init(&ctx->out_sem); + sm_ipt_os_mutex_init(&ctx->out_mutex); + + ctx->out.allocable_size = (((ctx->os_ctx.out_total_size - (2 * WORD_SIZE + \ + 2 * NUM_BLOCKS + 2)) / ALLOCABLE_MULTIPLY) * ALLOCABLE_MULTIPLY); + ctx->out.queue_items = ((ctx->os_ctx.out_total_size - ctx->out.allocable_size - 1) - \ + 2 * WORD_SIZE); + ctx->out.block_size = (ctx->out.allocable_size / NUM_BLOCKS); + ctx->in.allocable_size = (((ctx->os_ctx.in_total_size - (2 * WORD_SIZE + \ + 2 * NUM_BLOCKS + 2)) / ALLOCABLE_MULTIPLY) * ALLOCABLE_MULTIPLY); + ctx->in.queue_items = ((ctx->os_ctx.in_total_size - ctx->in.allocable_size - 1) - \ + 2 * WORD_SIZE); + ctx->in.block_size = (ctx->in.allocable_size / NUM_BLOCKS); + + ctx->out.allocable = (uint8_t *)ctx->os_ctx.out_shmem_ptr; + ctx->out.queue_tx = (uint32_t *)&ctx->out.allocable[ctx->out.allocable_size]; + ctx->out.queue_rx = (uint32_t *)&ctx->out.allocable[ctx->out.allocable_size + WORD_SIZE]; + ctx->out.queue = (uint8_t *)&ctx->out.allocable[ctx->out.allocable_size + 2 * WORD_SIZE]; + ctx->out.handshake = (uint8_t *)&ctx->out.allocable[ctx->os_ctx.out_total_size - 1]; + ctx->in.allocable = (uint8_t *)ctx->os_ctx.in_shmem_ptr; + ctx->in.queue_tx = (uint32_t *)&ctx->in.allocable[ctx->in.allocable_size]; + ctx->in.queue_rx = (uint32_t *)&ctx->in.allocable[ctx->in.allocable_size + WORD_SIZE]; + ctx->in.queue = (uint8_t *)&ctx->in.allocable[ctx->in.allocable_size + 2 * WORD_SIZE]; + ctx->in.handshake = (uint8_t *)&ctx->in.allocable[ctx->os_ctx.in_total_size - 1]; + + handshake_step(ctx, 0x32, 0x43); + handshake_step(ctx, 0x43, 0xF6); + + *ctx->out.queue_tx = 0; + *ctx->out.queue_rx = 0; + *ctx->in.queue_tx = 0; + *ctx->in.queue_rx = 0; + + sm_ipt_os_signal_handler(&ctx->os_ctx, signal_received); + + handshake_step(ctx, 0xF6, 0xA8); + handshake_step(ctx, 0xA8, 0xA8); + + SM_IPT_INF("Initialized succesfully"); + SM_IPT_INF("Max TX size: %d", ctx->out.allocable_size); + SM_IPT_INF("Max RX size: %d", ctx->in.allocable_size); + return 0; +} diff --git a/sm_ipt/template/sm_ipt_log_tmpl.h b/sm_ipt/template/sm_ipt_log_tmpl.h new file mode 100644 index 0000000000..112382d315 --- /dev/null +++ b/sm_ipt/template/sm_ipt_log_tmpl.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2021 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#ifndef SM_IPT_LOG_H_ +#define SM_IPT_LOG_H_ + +/* + * THIS IS A TEMPLATE FILE. + * It should be copied to a suitable location within the host environment into + * which Remote Procedure serialization is integrated, and the following macros + * should be provided with appropriate implementations. + */ + +/** + * @defgroup sm_ipt_log Logging functionality for SM IPT + * @{ + * @ingroup sm_ipt + * + * @brief Logging functionality for SM IPT + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Macro for logging a message with the severity level ERR. + * + * @param ... printf-style format string, optionally followed by arguments + * to be formatted and inserted in the resulting string. + */ +#define SM_IPT_ERR(...) + +/** + * @brief Macro for logging a message with the severity level WRN. + * + * @param ... printf-style format string, optionally followed by arguments + * to be formatted and inserted in the resulting string. + */ +#define SM_IPT_WRN(...) + +/** + * @brief Macro for logging a message with the severity level INF. + * + * @param ... printf-style format string, optionally followed by arguments + * to be formatted and inserted in the resulting string. + */ +#define SM_IPT_INF(...) + +/** + * @brief Macro for logging a message with the severity level DBG. + * + * @param ... printf-style format string, optionally followed by arguments + * to be formatted and inserted in the resulting string. + */ +#define SM_IPT_DBG(...) + + +#ifdef __cplusplus +} +#endif + +/** + *@} + */ + +#endif /* SM_IPT_LOG_H_ */ diff --git a/sm_ipt/template/sm_ipt_os_tmpl.h b/sm_ipt/template/sm_ipt_os_tmpl.h new file mode 100644 index 0000000000..5773d0449c --- /dev/null +++ b/sm_ipt/template/sm_ipt_os_tmpl.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2021 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#ifndef SM_IPT_OS_H_ +#define SM_IPT_OS_H_ + +/* + * THIS IS A TEMPLATE FILE. + * It should be copied to a suitable location within the host environment into + * which Shared memory inter-processor transport is integrated, and the following + * macros and structures should be provided with appropriate implementations. + */ + +/** + * @defgroup sm_ipt_os OS-dependent functionality for SM IPT + * @{ + * @ingroup sm_ipt_os + * + * @brief OS-dependent functionality for SM IPT + */ + +#ifdef __cplusplus +extern "C" { +#endif + + +/* --------------- Shared memory information --------------- */ + +/** @brief This struct holds all the data required by + * os layer to function. + */ +struct sm_ipt_os_ctx { + void *out_shmem_ptr; + void *in_shmem_ptr; + uint32_t out_total_size; + uint32_t in_total_size; + some_type other_os_context_data; +}; + +/** @brief Assertion function. + */ +#define SM_IPT_ASSERT(_expr) __ASSERT_FUNCTION(_expr) + +/** @brief Full memory barier. + */ +#define NRF_RPC_OS_MEMORY_BARIER() __sync_synchronize() + +/** @brief Returns pointer to the container struct. + */ +#define SM_IPT_OS_GET_CONTAINTER(ptr, type, field) SOME_CONTAINER_MACRO(ptr, type, field) + +/* --------------- Inter-core signaling --------------- */ + +/** @brief Signal other core that new data is waiting. + */ +void nrf_rpc_os_signal(struct sm_ipt_os_ctx *os_ctx); + +/** @brief Sets callback that will be called when the other core + * has signaled incoming data. + */ +void nrf_rpc_os_signal_handler(struct sm_ipt_os_ctx *os_ctx, + void (*handler)(struct sm_ipt_os_ctx *)); + +/** @brief Initializes shared memory and returns all pointers + * used by sm_ipt. + */ +int sm_ipt_os_init(struct sm_ipt_os_ctx *os_ctx); + +/* --------------- Atomics --------------- */ + +/* This atomic type must be 32 bits in size. + */ +typedef some_type nrf_rpc_os_atomic_t; + +/* Perform the operation suggested by the name, and return the value that + * had previously been in *atomic. + */ +uint32_t nrf_rpc_os_atomic_or(nrf_rpc_os_atomic_t *atomic, uint32_t value); +uint32_t nrf_rpc_os_atomic_and(nrf_rpc_os_atomic_t *atomic, uint32_t value); +uint32_t nrf_rpc_os_atomic_get(nrf_rpc_os_atomic_t *atomic); + +/* --------------- Mutexes --------------- */ + +typedef some_type nrf_rpc_os_mutex_t; +void nrf_rpc_os_mutex_init(nrf_rpc_os_mutex_t *mutex); +void nrf_rpc_os_lock(nrf_rpc_os_mutex_t *mutex); +void nrf_rpc_os_unlock(nrf_rpc_os_mutex_t *mutex); + +/* --------------- Semaphores --------------- */ + +typedef some_type nrf_rpc_os_sem_t; +void nrf_rpc_os_sem_init(nrf_rpc_os_sem_t *sem); +void nrf_rpc_os_take(nrf_rpc_os_sem_t *sem); +void nrf_rpc_os_give(nrf_rpc_os_sem_t *sem); + +/* --------------- Other OS functionality --------------- */ + +void nrf_rpc_os_yield(void); +void nrf_rpc_os_fatal(void); +int nrf_rpc_os_clz64(uint64_t value); +int nrf_rpc_os_clz32(uint32_t value); + +#ifdef __cplusplus +} +#endif + +/** + *@} + */ + +#endif /* SM_IPT_OS_H_ */