1 /***************************************************************************//**
2 * \file cyhal_ipc.h
3 *
4 * \brief
5 * Provides a high level interface for interacting with the Infineon IPC.
6 * This interface abstracts out the chip specific details. If any chip specific
7 * functionality is necessary, or performance is critical the low level functions
8 * can be used directly.
9 *
10 ********************************************************************************
11 * \copyright
12 * Copyright 2022 Cypress Semiconductor Corporation (an Infineon company) or
13 * an affiliate of Cypress Semiconductor Corporation
14 *
15 * SPDX-License-Identifier: Apache-2.0
16 *
17 * Licensed under the Apache License, Version 2.0 (the "License");
18 * you may not use this file except in compliance with the License.
19 * You may obtain a copy of the License at
20 *
21 *     http://www.apache.org/licenses/LICENSE-2.0
22 *
23 * Unless required by applicable law or agreed to in writing, software
24 * distributed under the License is distributed on an "AS IS" BASIS,
25 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
26 * See the License for the specific language governing permissions and
27 * limitations under the License.
28 *******************************************************************************/
29 
30 /**
31 * \addtogroup group_hal_ipc IPC (Inter-Processor Communication)
32 * \ingroup group_hal
33 * \{
34 * High level interface for communicating between processors on a multi-core device.
35 *
36 * The IPC driver allows communication between multiple CPUs or between multiple tasks
37 * operating in different domains within a single CPU. It supports binary semaphores
38 * and message queues, similar to how they are used for task interactions in an RTOS envrionment.
39 *
40 * \section subsection_ipc_features Features
41 * * Binary semaphore for resource access control and general signalling
42 * * Message queue for passing data between tasks/cores
43 *
44 * \section subsection_ipc_quickstart Quick Start
45 * For binary semaphores, initialize the semaphore for the task/CPU. Then take/give the semaphore.
46 * For queues, only one task/CPU may initialize a queue. Other tasks/CPUs then get the handle
47 * of the created queue. Use the get/put functions to take out or put in items to the queue.
48 *
49 * \section section_ipc_snippets Code Snippets
50 *
51 * \subsection subsection_ipc_snippet1 Snippet 1: Binary semaphore example
52 * \snippet hal_ipc.c snippet_cyhal_ipc_semaphore
53 *
54 * \subsection subsection_ipc_snippet2 Snippet 2: Message queue example
55 * \snippet hal_ipc.c snippet_cyhal_ipc_queue
56 */
57 
58 #pragma once
59 
60 #include "cyhal_hw_types.h"
61 #include <stdint.h>
62 #include <stdbool.h>
63 #include "cy_result.h"
64 #include "cyhal_hw_types.h"
65 
66 #if defined(__cplusplus)
67 extern "C" {
68 #endif
69 
70 /** \addtogroup group_hal_results_ipc IPC HAL Results
71  *  IPC specific return codes
72  *  \ingroup group_hal_results
73  *  \{ *//**
74  */
75 
76 /** Invalid parameter error */
77 #define CYHAL_IPC_RSLT_ERR_INVALID_PARAMETER           \
78     (CY_RSLT_CREATE_EX(CY_RSLT_TYPE_ERROR, CY_RSLT_MODULE_ABSTRACTION_HAL, CYHAL_RSLT_MODULE_IPC, 0))
79 /** Provided queue number already used */
80 #define CYHAL_IPC_RSLT_ERR_QUEUE_NUM_IN_USE            \
81     (CY_RSLT_CREATE_EX(CY_RSLT_TYPE_ERROR, CY_RSLT_MODULE_ABSTRACTION_HAL, CYHAL_RSLT_MODULE_IPC, 1))
82 /** Queue is full */
83 #define CYHAL_IPC_RSLT_ERR_QUEUE_FULL                  \
84     (CY_RSLT_CREATE_EX(CY_RSLT_TYPE_ERROR, CY_RSLT_MODULE_ABSTRACTION_HAL, CYHAL_RSLT_MODULE_IPC, 2))
85 /** Queue is empty */
86 #define CYHAL_IPC_RSLT_ERR_QUEUE_EMPTY                 \
87     (CY_RSLT_CREATE_EX(CY_RSLT_TYPE_ERROR, CY_RSLT_MODULE_ABSTRACTION_HAL, CYHAL_RSLT_MODULE_IPC, 3))
88 /** Queue object is not found */
89 #define CYHAL_IPC_RSLT_ERR_QUEUE_NOT_FOUND             \
90     (CY_RSLT_CREATE_EX(CY_RSLT_TYPE_ERROR, CY_RSLT_MODULE_ABSTRACTION_HAL, CYHAL_RSLT_MODULE_IPC, 4))
91 /** IPC interrupt was enabled for one of the cores, but was not (yet) handled */
92 #define CYHAL_IPC_RSLT_ERR_ISR_WAS_NOT_HANDLED         \
93     (CY_RSLT_CREATE_EX(CY_RSLT_TYPE_ERROR, CY_RSLT_MODULE_ABSTRACTION_HAL, CYHAL_RSLT_MODULE_IPC, 5))
94 /** Operation can't be performed in ISR context */
95 #define CYHAL_IPC_RSLT_ERR_CANT_OPERATE_IN_ISR         \
96     (CY_RSLT_CREATE_EX(CY_RSLT_TYPE_ERROR, CY_RSLT_MODULE_ABSTRACTION_HAL, CYHAL_RSLT_MODULE_IPC, 6))
97 
98 /**
99  * \}
100  */
101 
102 /** This define can be used as timeout argument for the IPC HAL driver functions, that take timeout
103  * as input parameter, in order to make function never time out (wait forever) */
104 #define CYHAL_IPC_NEVER_TIMEOUT (0xFFFFFFFFUL)
105 
106 /** Flags enum of IPC events. Multiple events can be enabled via \ref cyhal_ipc_queue_enable_event and
107  * the callback from \ref cyhal_ipc_queue_register_callback will be run to notify. */
108 typedef enum
109 {
110     CYHAL_IPC_NO_INTR           = 0,      //!< No interrupt
111     CYHAL_IPC_QUEUE_WRITE       = 1 << 0, //!< New item was written to the queue
112     CYHAL_IPC_QUEUE_READ        = 1 << 1, //!< New item was read from the queue
113     CYHAL_IPC_QUEUE_FULL        = 1 << 2, //!< Queue is full
114     CYHAL_IPC_QUEUE_EMPTY       = 1 << 3, //!< Queue is empty
115     CYHAL_IPC_QUEUE_RESET       = 1 << 4, //!< Queue was reset
116 } cyhal_ipc_event_t;
117 
118 /** Event handler for IPC interrupts */
119 typedef void (*cyhal_ipc_event_callback_t)(void *callback_arg, cyhal_ipc_event_t event);
120 
121 /** IPC queue structure element
122 */
123 typedef struct cyhal_ipc_queue_s
124 {
125     uint32_t channel_num;       //!< IPC channel number (e.g. CYHAL_IPC_CHAN_0) (populated by user during initialization)
126     uint32_t queue_num;         //!< Queue number (populated by user during initialization)
127     void *queue_pool;           //!< Pointer to the queue pool (populated by user during initialization). This is pointer to shared memory, in which queue elements will be stored. CYHAL_IPC_QUEUE_POOL_ALLOC macro can be used to allocate memory for pool.
128     uint32_t num_items;         //!< Maximum number of items allowed in the queue (populated by user during initialization)
129     uint32_t item_size;         //!< Size of each item in the queue (populated by user during initialization)
130     uint32_t curr_items;        //!< Current number of items in the queue (not expected to be modified by user)
131     void *queue_head;           //!< Pointer to the queue head in circular buffer (not expected to be modified by user)
132     void *queue_tail;           //!< Pointer to the queue tail in circular buffer (not expected to be modified by user)
133     uint32_t triggered_events;  //!< Events, that were triggered by latest performed operation (not expected to be modified by user)
134     struct cyhal_ipc_queue_s *next_queue_obj;   //!< Pointer to the next queue object (not expected to be modified by user)
135 #if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
136     uint32_t padding[6];        //!< Necessary to make sure the total size is a multiple of __SCB_DCACHE_LINE_SIZE
137 #endif
138 }cyhal_ipc_queue_t;
139 
140 /** Creates a single semaphore based on a given number.
141  *
142  * This function must be called by all tasks/CPUs accessing the semaphore.
143  * @param[out] obj              Pointer to an IPC object. The caller must allocate the memory for this object but the
144  * init function will initialize its contents.
145  * @param[in] semaphore_num     The semaphore number to initialize.
146  * @param[in] preemptable       Allows whether the semaphore can be preempted by another task.
147  * @return The status of the init request
148  */
149 cy_rslt_t cyhal_ipc_semaphore_init(cyhal_ipc_t *obj, uint32_t semaphore_num, bool preemptable);
150 
151 /** Frees the IPC semaphore.
152  *
153  * This function frees the resources associated with the semaphore.
154  * @param[in, out] obj          The IPC object.
155  */
156 void cyhal_ipc_semaphore_free(cyhal_ipc_t *obj);
157 
158 /** Takes/acquires a semaphore.
159  *
160  * If the semaphore is available, it is acquired and this function and returns.
161  * This function has a timeout argument (in microseconds). If the semaphore is not available, it blocks until it
162  * times out or succeeds in acquiring it.
163  * @param[in] obj               The IPC object.
164  * @param[in] timeout_us        Timeout in microseconds. Value 0 can be used if no timeout needed while
165  * \ref CYHAL_IPC_NEVER_TIMEOUT can be used to make function block until semaphore is successfully taken.
166  * @return The status of the take request
167  */
168 cy_rslt_t cyhal_ipc_semaphore_take(cyhal_ipc_t *obj, uint32_t timeout_us);
169 
170 /** Gives/releases a semaphore.
171  *
172  * The semaphore is released allowing other tasks waiting on the semaphore to take it.
173  * @param[in] obj               The IPC object.
174  * @return The status of the give request
175  */
176 cy_rslt_t cyhal_ipc_semaphore_give(cyhal_ipc_t *obj);
177 
178 /** Creates a new queue for a given IPC channel based on the given queue number and other parameters.
179  *
180  * This function requires \ref cyhal_ipc_queue_t (queue handle) pointer to shared memory. Some elements of cyhal_ipc_queue_t
181  * structure are expected to be filled by user. One key element of the structure to be filled by user is a pointer to
182  * the queue pool allocated in the shared memory.
183  * Queue handle is used by other tasks/CPUs to refer to the queue. Note that this function must be called only by one
184  * of the tasks/CPUs for the same IPC channel. This CPU can call the function multiple times for the same IPC
185  * channel, but with a different queue number.
186  * \note CYHAL_IPC_QUEUE_HANDLE_ALLOC and CYHAL_IPC_QUEUE_POOL_ALLOC macro can be used in order to allocate
187  * memory for (respectively) queue handle (cyhal_ipc_queue_t) and queue pool in shared section. Please refer to
188  * \ref subsection_ipc_snippet2 for initialization guidance.
189  * @param[out] obj              Pointer to an IPC object. The caller must allocate the memory for this object
190  * but the init function will initialize its contents.
191  * @param[in] queue_handle      Queue handle. Fields channel_num, queue_num, queue_pool, num_items and item_size
192  * are expected to be filled by user before initialization. Please refer to \ref subsection_ipc_snippet2 for
193  * initialization guidance.
194  * @return The status of the init request
195  */
196 cy_rslt_t cyhal_ipc_queue_init(cyhal_ipc_t *obj, cyhal_ipc_queue_t *queue_handle);
197 
198 /** Frees the queue.
199  *
200  * This operation only removes the queue handle from the list of available queues. The queue pool and the queue
201  * handle allocated in the shared memory needs to be freed (if dynamically allocated) by the application.
202  * @param[in, out] obj          The IPC object
203  */
204 void cyhal_ipc_queue_free(cyhal_ipc_t *obj);
205 
206 /** Gets a handle pointer for a given IPC channel and queue number.
207  *
208  * This function should be called by other tasks/CPUs that have not called the initialization function.
209  * @param[out] obj              The IPC object handle.
210  * @param[in] channel_num       IPC channel to use for the queue messaging.
211  * @param[in] queue_num         Queue number.
212  * @return The status of the get handle request
213  */
214 cy_rslt_t cyhal_ipc_queue_get_handle(cyhal_ipc_t *obj, uint32_t channel_num, uint32_t queue_num);
215 
216 /** Registers a callback to be executed when certain events occur.
217  *
218  * @param[in] obj               The IPC object.
219  * @param[in] callback          The callback handler which will be invoked when an event triggers.
220  * @param[in] callback_arg      Generic argument that will be provided to the callback when called.
221  */
222 void cyhal_ipc_queue_register_callback(cyhal_ipc_t *obj, cyhal_ipc_event_callback_t callback, void *callback_arg);
223 
224 /** Enables which events trigger the callback execution.
225  *
226  * It can trigger when a new item is written to the queue, read from the queue, when the queue becomes full,
227  * when the queue becomes empty or when there is a reset. Note that these events might execute callbacks
228  * associated to all queues that belong to an IPC channel. When defining the ISR priority, the last call to
229  * this function overwrites the priority for all queue callbacks registered to the same IPC channel.
230  * @param[in] obj               The IPC object
231  * @param[in] event             The IPC event type
232  * @param[in] intr_priority     The priority for NVIC interrupt events
233  * @param[in] enable            True to turn on specified events, False to turn off
234  */
235 void cyhal_ipc_queue_enable_event(cyhal_ipc_t *obj, cyhal_ipc_event_t event, uint8_t intr_priority, bool enable);
236 
237 /** Adds one item to the queue.
238  *
239  * This function can be called by any task/CPU. This function has a timeout argument (in microseconds).
240  * If the queue is full, it stays there until it times out or the queue is no longer full.
241  * This function can be blocking or non-blocking (timeout set to ZERO).
242  * @param[in] obj               The IPC object
243  * @param[in] msg               Location of message queue item
244  * @param[in] timeout_us        Timeout in microseconds. Value 0 can be used if no timeout needed while
245  * \ref CYHAL_IPC_NEVER_TIMEOUT can be used to make function block until element is successfully put into the queue.
246  * @return The status of the put request
247  */
248 cy_rslt_t cyhal_ipc_queue_put(cyhal_ipc_t *obj, void *msg, uint32_t timeout_us);
249 
250 /** Removes one item from the queue.
251  *
252  * This function can be called by any task/CPU. This function has a timeout argument (in microseconds).
253  * If the queue is empty, it stays there until it times out or the queue receives a new item.
254  * This function can be blocking or non-blocking (timeout set to ZERO).
255  * @param[in] obj               The IPC object
256  * @param[out] msg              Location of message queue item
257  * @param[in] timeout_us        Timeout in microseconds. Value 0 can be used if no timeout needed while
258  * \ref CYHAL_IPC_NEVER_TIMEOUT can be used to make function block until element is successfully taken from the queue.
259  * @return The status of the get request
260  */
261 cy_rslt_t cyhal_ipc_queue_get(cyhal_ipc_t *obj, void *msg, uint32_t timeout_us);
262 
263 /** Returns how many items are in the queue.
264  *
265  * This function can be called by any task/CPU.
266  * @param[in] obj               The IPC object
267  * @return Number of items in the queue
268  */
269 uint32_t cyhal_ipc_queue_count(cyhal_ipc_t *obj);
270 
271 /** Clear all the items in the queue.
272  *
273  * This function can be called by the any task/CPU.
274  * @param[in] obj               The IPC object
275  * @return The status of the reset request
276  */
277 cy_rslt_t cyhal_ipc_queue_reset(cyhal_ipc_t *obj);
278 
279 #if defined(__cplusplus)
280 }
281 #endif
282 
283 #ifdef CYHAL_IPC_IMPL_HEADER
284 #include CYHAL_IPC_IMPL_HEADER
285 #endif /* CYHAL_IPC_IMPL_HEADER */
286 
287 /** \} group_hal_ipc */
288