LPCOpen Platform
LPCOpen Platform for NXP LPC Microcontrollers
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
ipc_msg.c
Go to the documentation of this file.
1 /*
2  * @brief LPC43XX Inter Processor Communication(IPC) functions
3  *
4  * @note
5  * Copyright(C) NXP Semiconductors, 2012
6  * All rights reserved.
7  *
8  * @par
9  * Software that is described herein is for illustrative purposes only
10  * which provides customers with programming information regarding the
11  * LPC products. This software is supplied "AS IS" without any warranties of
12  * any kind, and NXP Semiconductors and its licensor disclaim any and
13  * all warranties, express or implied, including all implied warranties of
14  * merchantability, fitness for a particular purpose and non-infringement of
15  * intellectual property rights. NXP Semiconductors assumes no responsibility
16  * or liability for the use of the software, conveys no license or rights under any
17  * patent, copyright, mask work right, or any other intellectual property rights in
18  * or to any products. NXP Semiconductors reserves the right to make changes
19  * in the software without notification. NXP Semiconductors also makes no
20  * representation or warranty that such application will be suitable for the
21  * specified use without further testing or modification.
22  *
23  * @par
24  * Permission to use, copy, modify, and distribute this software and its
25  * documentation is hereby granted, under NXP Semiconductors' and its
26  * licensor's relevant copyrights in the software, without fee, provided that it
27  * is used in conjunction with NXP Semiconductors microcontrollers. This
28  * copyright, permission, and disclaimer notice must appear in all copies of
29  * this code.
30  */
31 
32 #include <string.h>
33 #include "lpc43xx_dualcore_config.h"
34 #include "ipc_msg.h"
35 
36 #if defined(OS_FREE_RTOS)
37 #include "FreeRTOS.h"
38 #include "semphr.h"
39 
40 #elif defined(OS_UCOS_III)
41 #include "os.h"
42 
43 #endif
44 
49 /*****************************************************************************
50  * Private types/enumerations/variables
51  ****************************************************************************/
52 
53 #define SHMEMM0 LOCATE_AT(SHARED_MEM_M0)
54 #define SHMEMM4 LOCATE_AT(SHARED_MEM_M4)
55 
56 /*
57  * M0 write queue
58  * This is the queue which will be written by M0 and be read by M4
59  * NOTE:
60  * This variable is put in SHMEMM0 section which will
61  * be linked to shared memory region by linker script.
62  */
63 SHMEMM0 static struct ipc_queue queue_m0 = {0};
64 
65 /*
66  * M4 write queue
67  * This is the queue which will be written by M4 and be read by M0
68  * NOTE:
69  * This variable is put in SHMEMM4 section which will
70  * be linked to shared memory region by linker script.
71  */
72 SHMEMM4 static struct ipc_queue queue_m4 = {0};
73 
74 /*
75  * IRQ priority of IPC interrupt
76  * Currently this has the highest priority
77  */
78 #define IPC_IRQ_Priority IRQ_PRIO_IPC
79 
80 #ifdef CORE_M4
81 /*
82  * M4 read queue pointer
83  * M4 reads from this message queue based on the
84  * tail index.
85  */
86 static struct ipc_queue *qrd = &queue_m0;
87 /*
88  * M4 write queue pointer
89  * M4 writes from this message queue based on the
90  * head index.
91  */
92 static struct ipc_queue *qwr = &queue_m4;
93 
94 #define IPC_IRQHandler MX_CORE_IRQHandler
95 #define ClearTXEvent Chip_CREG_ClearM0Event
96 #define IPC_IRQn M0CORE_IRQn
97 
98 #elif defined(CORE_M0)
99 static struct ipc_queue *qrd = &queue_m4;
100 static struct ipc_queue *qwr = &queue_m0;
101 #define IPC_IRQHandler MX_CORE_IRQHandler
102 #define ClearTXEvent Chip_CREG_ClearM4Event
103 #define IPC_IRQn M0_M4CORE_IRQn
104 
105 #else
106 #error "For LPC43XX, CORE_M0 or CORE_M4 must be defined!"
107 #endif
108 
109 /* FreeRTOS functions */
110 #ifdef OS_FREE_RTOS
111 /* FreeRTOS semaphores for event handling */
112 static xSemaphoreHandle event_tx, event_rx;
113 
114 #elif defined(OS_UCOS_III)
115 static OS_SEM event_tx, event_rx;
116 #endif
117 
118 /*****************************************************************************
119  * Public types/enumerations/variables
120  ****************************************************************************/
121 
122 /*****************************************************************************
123  * Private functions
124  ****************************************************************************/
125 
126 #ifdef OS_FREE_RTOS
127 /*****************************************************************************
128  * FreeRTOS functions
129  ****************************************************************************/
130 
131 /* OS specific event hanlder for FreeRTOS */
132 static void os_event_handler(void)
133 {
134  portBASE_TYPE wake1 = pdFALSE, wake2 = pdFALSE;
135 
136  if (event_rx && !QUEUE_IS_EMPTY(qrd)) {
137  xSemaphoreGiveFromISR(event_rx, &wake1);
138  }
139 
140  if (event_tx && !QUEUE_IS_FULL(qwr)) {
141  xSemaphoreGiveFromISR(event_tx, &wake2);
142  }
143 
144  portEND_SWITCHING_ISR(wake1 || wake2);
145 }
146 
147 /* Misc Init function that initializes OS semaphores */
148 static void ipc_misc_init(void)
149 {
150  vSemaphoreCreateBinary(event_tx);
151  vSemaphoreCreateBinary(event_rx);
152 
153  if (!event_tx || !event_rx) {
154  DEBUGSTR("ERROR: Unable to create FreeRTOS IPC event semaphores.\r\n");
155  while (1) { /* DIE: unable to create semaphores */
156  }
157  }
158 }
159 
160 /* Macro that waits till TX/RX event to happen */
161 #define ipc_wait_event(evt, sem) while ((evt)) xSemaphoreTake(sem, 100)
162 /* Macro that waits till TX/RX event to happen before tout (timeout) */
163 #define ipc_wait_event_tout(evt, tout, sem) \
164  do { \
165  while ((evt)) { \
166  if ((evt) && xSemaphoreTake(sem, tout) != pdTRUE) \
167  {tout = 0; break; }} \
168  } while (0)
169 
170 #elif defined(OS_UCOS_III)
171 /*****************************************************************************
172  * uCOS-III functions
173  ****************************************************************************/
174 
175 static void ipc_misc_init(void)
176 {
177  OS_ERR ret;
178  OSSemCreate(&event_tx, "TX Sema", 0, &ret);
179  if (ret != OS_ERR_NONE) {
180  while (1) {}
181  }
182  OSSemCreate(&event_rx, "RX Sema", 0, &ret);
183  if (ret != OS_ERR_NONE) {
184  while (1) {}
185  }
186 }
187 
188 static void os_event_handler(void)
189 {
190  OS_ERR ret;
191  if (!QUEUE_IS_EMPTY(qrd)) {
192  OSSemPost(&event_rx, OS_OPT_POST_ALL, &ret);
193  }
194 
195  if (!QUEUE_IS_FULL(qwr)) {
196  OSSemPost(&event_tx, OS_OPT_POST_ALL, &ret);
197  }
198 }
199 
200 /* Wait for the event */
201 #define ipc_wait_event(evt, sem) \
202  while ((evt)) {OS_ERR ret; \
203  OSSemPend(&(sem), (OS_TICK) 100, OS_OPT_PEND_BLOCKING, (CPU_TS *) 0, &ret); }
204 #define ipc_wait_event_tout(evt, tout, sem) \
205  do { \
206  while ((evt)) { \
207  OS_ERR ret; \
208  OSSemPend(&(sem), (OS_TICK) tout, OS_OPT_PEND_BLOCKING, (CPU_TS *) 0, &ret); \
209  if ((evt) && ret == OS_ERR_TIMEOUT) {tout = 0; break; } \
210  } \
211  } while (0)
212 
213 #else
214 /*****************************************************************************
215  * uCOS-III functions
216  ****************************************************************************/
217 
218 static void os_event_handler(void)
219 {
220  /* Nothing to do here */
221 }
222 
223 /* Wait for the event */
224 #define ipc_wait_event(evt, sem) while ((evt))
225 
226 #define ipc_wait_event_tout(evt, tout, sem) \
227  do { \
228  uint32_t cnt = Chip_RIT_GetCounter() + (tout * (SystemCoreClock / 1000)); \
229  if (cnt + 5000 < cnt) {cnt += 5000; } \
230  while ((evt) && Chip_RIT_GetCounter() < cnt) {} \
231  if (evt) {tout = 0; } \
232  } while (0)
233 
234 static void ipc_misc_init(void)
235 {}
236 
237 #endif
238 
239 /*
240  * Initiate interrupt on other processor
241  * Upon calling this function generates and interrupt on the other
242  * core. Ex. if called from M0 core it generates interrupt on M4 core
243  * and vice versa.
244  */
245 static void ipc_send_signal(void)
246 {
247  __DSB();
248  __SEV();
249 }
250 
251 /*****************************************************************************
252  * Public functions
253  ****************************************************************************/
254 
255 /* Function to send notificaton interrupt */
256 void IPC_msgNotify(void)
257 {
258  ipc_send_signal();
259 }
260 
261 /* Function to initialize the IPC message queue */
262 void IPC_initMsgQueue(void *data, int size, int count)
263 {
264  /* Sanity Check */
265  if (!size || !count || !data) {
266  DEBUGSTR("ERROR:IPC Queue size invalid parameters\r\n");
267  while (1) {}
268  }
269 
270  /* Check if size is a power of 2 */
271  if (count & (count - 1)) {
272  DEBUGSTR("ERROR:IPC Queue size not power of 2\r\n");
273  while (1) { /* BUG: Size must always be power of 2 */
274  }
275  }
276 
277  memset(qwr, 0, sizeof(*qwr));
278  ipc_misc_init();
279  qwr->count = count;
280  qwr->size = size;
281  qwr->data = data;
282  NVIC_SetPriority(IPC_IRQn, IPC_IRQ_Priority);
283  NVIC_EnableIRQ(IPC_IRQn);
284 }
285 
286 /* Function to push a message into queue with timeout */
287 int IPC_pushMsgTout(const void *data, int tout)
288 {
289  /* Check if write queue is initialized */
290  if (!qwr->size) {
291  return QUEUE_ERROR;
292  }
293 
294  if (tout == 0) {
295  /* Check if queue is full */
296  if (QUEUE_IS_FULL(qwr)) {
297  return QUEUE_FULL;
298  }
299  }
300  else if (tout < 0) {
301  /* Wait for read queue to have some data */
302  ipc_wait_event(QUEUE_IS_FULL(qwr), event_tx);
303  }
304  else {
305  /* Wait for read queue to have some data */
306  ipc_wait_event_tout(QUEUE_IS_FULL(qwr), tout, event_tx);
307  if (tout == 0) {
308  return QUEUE_TIMEOUT;
309  }
310  }
311 
312  memcpy(qwr->data + ((qwr->head & (qwr->count - 1)) * qwr->size), data, qwr->size);
313  qwr->head++;
314  ipc_send_signal();
315 
316  return QUEUE_INSERT;
317 }
318 
319 /* Function to read a message from queue with timeout */
320 int IPC_popMsgTout(void *data, int tout)
321 {
322 #ifdef EVENT_ON_RX
323  int raise_event = QUEUE_IS_FULL(qrd);
324 #endif
325 
326  if (!qrd->size) {
327  return QUEUE_ERROR;
328  }
329 
330  if (tout == 0) {
331  /* Check if read queue is empty */
332  if (QUEUE_IS_EMPTY(qrd)) {
333  return QUEUE_EMPTY;
334  }
335  }
336  else if (tout < 0) {
337  /* Wait for read queue to have some data */
338  ipc_wait_event(QUEUE_IS_EMPTY(qrd), event_rx);
339  }
340  else {
341  /* Wait for event or timeout */
342  ipc_wait_event_tout(QUEUE_IS_EMPTY(qrd), tout, event_rx);
343  if (tout == 0) {
344  return QUEUE_TIMEOUT;
345  }
346  }
347 
348  /* Pop the queue Item */
349  memcpy(data, qrd->data + ((qrd->tail & (qrd->count - 1)) * qrd->size), qrd->size);
350  qrd->tail++;
351 
352 #ifdef EVENT_ON_RX
353  if (raise_event) {
354  ipc_send_signal();
355  }
356 #endif
357  return QUEUE_VALID;
358 }
359 
360 /* Get number of pending items in queue */
361 int IPC_msgPending(int queue_write)
362 {
363  if (queue_write) {
364  return QUEUE_DATA_COUNT(qwr);
365  }
366  else {
367  return QUEUE_DATA_COUNT(qrd);
368  }
369 }
370 
379 #ifdef __IAR_SYSTEMS_ICC__
380 __weak void EVENT_IPC_Receive(void)
381 #else
382 void EVENT_IPC_Receive(void) __attribute__ ((weak));
383 
384 void EVENT_IPC_Receive(void)
385 #endif
386 {}
387 
392 void IPC_IRQHandler(void)
393 {
394  /* Clear the interrupt */
395  ClearTXEvent();
396 
397  /* Invoke OS Specific handler */
399 
400  /* Invoke handler */
402 }
403 
404 /* Function to convert IPC error number to string */
405 const char *IPC_strerror(int errnum)
406 {
407  static const char *ipc_errstr[] = {
408  "Queue Insert OK",
409  "Queue Pop OK/Valid",
410  "Queue is Full",
411  "Queue is Empty",
412  "Queue Error/Not initialized",
413  "Queue operation timed out",
414  };
415  if (errnum < 0) {
416  errnum = 1 - errnum;
417  }
418  return ipc_errstr[errnum];
419 }
420