1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
49
50 package ti.sdo.ipc;
51
52 import xdc.runtime.IHeap;
53 import xdc.runtime.Assert;
54 import xdc.runtime.Error;
55 import xdc.runtime.Diags;
56 import xdc.runtime.Log;
57 import xdc.runtime.IGateProvider;
58 import xdc.runtime.knl.ISync;
59
60 import ti.sysbios.syncs.SyncSem;
61
62 import ti.sdo.ipc.interfaces.IMessageQTransport;
63 import ti.sdo.utils.NameServer;
64 import ti.sdo.utils.List;
65
66 import xdc.rov.ViewInfo;
67
68 /*!
69 * ======== MessageQ ========
70 * Message-passing with queuing
71 *
72 * The MessageQ module supports the structured sending and receiving of
73 * variable length messages. This module can be used for homogeneous
74 * (DSP to DSP) or heterogeneous (Arm to DSP) multi-processor messaging.
75 *
76 * MessageQ provides more sophisticated messaging than other modules. It is
77 * typically used for complex situations such as multi-processor messaging.
78 *
79 * The following are key features of the MessageQ module:
80 * @p(blist)
81 * -Writers and readers can be relocated to another processor with no
82 * runtime code changes.
83 * -Timeouts are allowed when receiving messages.
84 * -Readers can determine the writer and reply back.
85 * -Receiving a message is deterministic when the timeout is zero.
86 * -Messages can reside on any message queue.
87 * -Supports zero-copy transfers.
88 * -Can send and receive from any type of thread.
89 * -Notification mechanism is specified by application.
90 * -Allows QoS (quality of service) on message buffer pools. For example,
91 * using specific buffer pools for specific message queues.
92 * @p
93 *
94 * Messages are sent and received by being placed on and removed from a
95 * message queue. A reader is a thread that gets (reads) messages from a
96 * message queue. A writer is a thread that puts (writes) a message to a
97 * message queue. Each message queue has one reader and can have many writers.
98 * A thread may read from or write to multiple message queues.
99 *
100 * Conceptually, the reader thread owns a message queue. The reader thread
101 * creates a message queue. The writer threads open a created message queue
102 * to get access to them.
103 *
104 * Message queues are identified by a system-wide unique name. Internally,
105 * MessageQ uses the {@link ti.sdo.utils.NameServer} module for managing
106 * these names. The names are used for opening a message queue.
107 *
108 * Messages must be allocated from the MessageQ module. Once a message is
109 * allocated, it can be sent to any message queue. Once a message is sent, the
110 * writer loses ownership of the message and should not attempt to modify the
111 * message. Once the reader receives the message, it owns the message. It
112 * may either free the message or re-use the message.
113 *
114 * Messages in a message queue can be of variable length. The only
115 * requirement is that the first field in the definition of a message must be a
116 * {@link #MsgHeader} structure. For example:
117 * @p(code)
118 * typedef struct MyMsg {
119 * MessageQ_MsgHeader header;
120 * ...
121 * } MyMsg;
122 * @p
123 *
124 * The MessageQ API uses the MessageQ_MsgHeader internally. Your application
125 * should not modify or directly access the fields in the MessageQ_MsgHeader.
126 *
127 * All messages sent via the MessageQ module must be allocated from a
128 * {@link xdc.runtime.IHeap} implementation. The heap can also be used for
129 * other memory allocation not related to MessageQ.
130 *
131 * An application can use multiple heaps. The purpose of having multiple
132 * heaps is to allow an application to regulate its message usage. For
133 * example, an application can allocate critical messages from one heap of fast
134 * on-chip memory and non-critical messages from another heap of slower
135 * external memory.
136 *
137 * The {@link #registerHeap} and {@link #registerHeapMeta} are APIs used to
138 * assign a MessageQ heapId to a heap. When allocating a message, the heapId
139 * is used, not the heap handle. This heapId is actually placed into the
140 * message (part of the {@link #MsgHeader}). Care must be taken when assigning
141 * heapIds. Refer to the {@link #registerHeap} and {@link #registerHeapMeta}
142 * descriptions for more details.
143 *
144 * MessageQ also supports the usage of messages that are not allocated via the
145 * {@link #alloc} function. Please refer to the {@link #staticMsgInit}
146 * function description for more details.
147 *
148 * MessageQ supports reads/writes of different thread models. This is
149 * accomplished by having the creator of the message queue specify a
150 * {@link xdc.runtime.knl.ISync#Object} via the {@link #synchronizer}
151 * configuration parameter. The {@link xdc.runtime.knl.ISync#signal}
152 * portion of the ISync instance is called whenever the {@link #put}
153 * is called. The {@link xdc.runtime.knl.ISync#wait} portion is
154 * called in the {@link #get} if and only if there are no messages.
155 *
156 * Since ISyncs are binary, the reader must drain the message queue of all
157 * messages before waiting for another signal. For example, if the reader
158 * was a SYSBIOS Swi, the {@link xdc.runtime.knl.ISync} instance
159 * could be a SyncSwi. If a {@link #put} was called, the Swi_post() would
160 * be called. The Swi would run and it must call {@link #get} until no
161 * messages are returned.
162 *
163 * In a multiple processor system, MessageQ communicates to other
164 * processors via {@link ti.sdo.ipc.interfaces.IMessageQTransport} instances.
165 * MessageQ supports a high priority and a normal priority transport between
166 * any two processors. The IMessageQTransport instances are created via the
167 * {@link #SetupTransportProxy}. The instances are responsible for
168 * registering themselves with MessageQ. This is accomplished via the
169 * {@link #registerTransport} function.
170 */
171
172 @ModuleStartup
173 @InstanceInitError
174 @InstanceFinalize
175
176 module MessageQ
177 {
178 /*!
179 * ======== QueuesView ========
180 * @_nodoc
181 */
182 metaonly struct QueuesView {
183 String name;
184 UInt queueId;
185 }
186
187 /*!
188 * ======== MessagesView ========
189 * @_nodoc
190 */
191 metaonly struct MessagesView {
192 Int seqNum;
193 Int msgSize;
194 String priority;
195 String srcProc;
196 String replyProc;
197 String replyId;
198 Int msgId;
199 String heap;
200 Bool traceEnabled;
201 Int version;
202 }
203
204 /*!
205 * ======== ModuleView ========
206 * @_nodoc
207 */
208 metaonly struct ModuleView {
209 String heaps[];
210 String gate;
211 UInt16 nextSeqNum;
212 }
213
214 /*!
215 * ======== rovViewInfo ========
216 * @_nodoc
217 */
218 @Facet
219 metaonly config xdc.rov.ViewInfo.Instance rovViewInfo =
220 xdc.rov.ViewInfo.create({
221 viewMap: [
222 ['Queues',
223 {
224 type: xdc.rov.ViewInfo.INSTANCE,
225 viewInitFxn: 'viewInitQueues',
226 structName: 'QueuesView'
227 }
228 ],
229 ['Messages',
230 {
231 type: xdc.rov.ViewInfo.INSTANCE_DATA,
232 viewInitFxn: 'viewInitMessages',
233 structName: 'MessagesView'
234 }
235 ],
236 ['Module',
237 {
238 type: xdc.rov.ViewInfo.MODULE,
239 viewInitFxn: 'viewInitModule',
240 structName: 'ModuleView'
241 }
242 ]
243 ]
244 });
245
246 /*!
247 * ======== LM_setTrace ========
248 * Logged when setting the trace flag on a message
249 *
250 * This is logged when tracing on a message is set via
251 * {@link #setMsgTrace}.
252 */
253 config Log.Event LM_setTrace = {
254 mask: Diags.USER1,
255 msg: "LM_setTrace: Message 0x%x (seqNum = %d, srcProc = %d) traceFlag = %d"
256 };
257
258 /*!
259 * ======== LM_alloc ========
260 * Logged when allocating a message
261 *
262 * When the {@link #traceFlag} is true, all message allocations
263 * are logged.
264 */
265 config Log.Event LM_alloc = {
266 mask: Diags.USER1,
267 msg: "LM_alloc: Message 0x%x (seqNum = %d, srcProc = %d) was allocated"
268 };
269
270 /*!
271 * ======== LM_staticMsgInit ========
272 * Logged when statically initializing a message
273 *
274 * When the {@link #traceFlag} is true, all messages that
275 * are statically initialized via {@link #staticMsgInit} are logged.
276 */
277 config Log.Event LM_staticMsgInit = {
278 mask: Diags.USER1,
279 msg: "LM_staticMsgInit: Message 0x%x (seqNum = %d, srcProc = %d) was set in MessageQ_staticMsgInit"
280 };
281
282 /*!
283 * ======== LM_free ========
284 * Logged when freeing a message
285 *
286 * When the {@link #traceFlag} is true, all freeing of messages
287 * are logged. If an individual message's tracing was enabled
288 * via {@link #setMsgTrace}, the MessageQ_free is also logged.
289 */
290 config Log.Event LM_free = {
291 mask: Diags.USER1,
292 msg: "LM_free: Message 0x%x (seqNum = %d, srcProc = %d) was freed"
293 };
294
295 /*!
296 * ======== LM_putLocal ========
297 * Logged when a message is placed onto a local queue
298 *
299 * When the {@link #traceFlag} is true, all putting of messages
300 * are logged. If an individual message's tracing was enabled
301 * via {@link #setMsgTrace}, the MessageQ_put is also logged.
302 */
303 config Log.Event LM_putLocal = {
304 mask: Diags.USER1,
305 msg: "LM_putLocal: Message 0x%x (seqNum = %d, srcProc = %d) was placed onto queue 0x%x"
306 };
307
308 /*!
309 * ======== LM_putRemote ========
310 * Logged when a message is given to a transport
311 *
312 * When the {@link #traceFlag} is true, all putting of messages
313 * to a transport are logged. If an individual message's tracing
314 * was enabled via {@link #setMsgTrace}, the MessageQ_put is
315 * also logged.
316 */
317 config Log.Event LM_putRemote = {
318 mask: Diags.USER1,
319 msg: "LM_putRemote: Message 0x%x (seqNum = %d, srcProc = %d) was given to processor %d transport"
320 };
321
322 /*!
323 * ======== LM_rcvByTransport ========
324 * Logged when a transport receives an incoming message
325 *
326 * When the {@link #traceFlag} is true, all incoming messages
327 * are logged. If an individual message's tracing
328 * was enabled via {@link #setMsgTrace}, the receiving of a message is
329 * also logged.
330 */
331 config Log.Event LM_rcvByTransport = {
332 mask: Diags.USER1,
333 msg: "LM_rcvByTransport: Message 0x%x (seqNum = %d, srcProc = %d) was received"
334 };
335
336 /*!
337 * ======== LM_get ========
338 * Logged when a message is received off the queue
339 *
340 * When the {@link #traceFlag} is true, all getting of messages
341 * are logged. If an individual message's tracing
342 * was enabled via {@link #setMsgTrace}, the MessageQ_get is
343 * also logged.
344 */
345 config Log.Event LM_get = {
346 mask: Diags.USER1,
347 msg: "LM_get: Message 0x%x (seqNum = %d, srcProc = %d) was received by queue 0x%x"
348 };
349
350 /*! MessageQ ID */
351 typedef UInt32 QueueId;
352
353 /*!
354 * ======== SetupTransportProxy ========
355 * MessageQ transport setup proxy
356 */
357 proxy SetupTransportProxy inherits ti.sdo.ipc.interfaces.ITransportSetup;
358
359 /*!
360 * Message priority values. These must match the values defined in
361 * ti/ipc/MessageQ.h but are needed here for ROV.
362 */
363 const UInt NORMALPRI = 0;
364 const UInt HIGHPRI = 1;
365 const UInt RESERVEDPRI = 2;
366 const UInt URGENTPRI = 3;
367
368 /*!
369 * Assert raised when calling API with wrong handle
370 *
371 * Some APIs can only be called with an opened handle (e.g.
372 * {@link #close}. Some can only be called with a created handle
373 * (e.g. {@link #get}).
374 */
375 config Assert.Id A_invalidContext = {
376 msg: "A_invalidContext: Cannot call with an open/create handle"
377 };
378
379 /*!
380 * Assert raised when attempting to free a static message
381 */
382 config Assert.Id A_cannotFreeStaticMsg = {
383 msg: "A_cannotFreeStaticMsg: Cannot call MessageQ_free with static msg"
384 };
385
386 /*!
387 * Assert raised when an invalid message is supplied
388 */
389 config Assert.Id A_invalidMsg = {
390 msg: "A_invalidMsg: Invalid message"
391 };
392
393 /*!
394 * Assert raised when an invalid queueId is supplied
395 */
396 config Assert.Id A_invalidQueueId = {
397 msg: "A_invalidQueueId: Invalid queueId is used"
398 };
399
400 /*!
401 * Assert raised when using an invalid heapId
402 */
403 config Assert.Id A_heapIdInvalid = {
404 msg: "A_heapIdInvalid: heapId is invalid"
405 };
406
407 /*!
408 * Assert raised when using an invalid procId
409 */
410 config Assert.Id A_procIdInvalid = {
411 msg: "A_procIdInvalid: procId is invalid"
412 };
413
414 /*!
415 * Assert raised for an invalid MessageQ object
416 */
417 config Assert.Id A_invalidObj = {
418 msg: "A_invalidObj: an invalid obj is used"
419 };
420
421 /*!
422 * Assert raised for an invalid parameter
423 */
424 config Assert.Id A_invalidParam = {
425 msg: "A_invalidParam: an invalid parameter was passed in"
426 };
427
428 /*!
429 * Assert raised when attempting to send a message to a core
430 * where a transport has not been registered.
431 */
432 config Assert.Id A_unregisteredTransport = {
433 msg: "A_unregisteredTransport: transport is not registered"
434 };
435
436 /*!
437 * Assert raised when attempting to unblock a remote MessageQ or one that
438 * has been configured with a non-blocking synchronizer
439 */
440 config Assert.Id A_invalidUnblock = {
441 msg: "A_invalidUnblock: Trying to unblock a remote MessageQ or a queue with non-blocking synchronizer"
442 };
443
444 /*!
445 * Error raised if all the message queue objects are taken
446 */
447 config Error.Id E_maxReached = {
448 msg: "E_maxReached: All objects in use. MessageQ.maxRuntimeEntries is %d"
449 };
450
451 /*!
452 * Error raised when heapId has not been registered
453 */
454 config Error.Id E_unregisterHeapId = {
455 msg: "E_unregisterHeapId: Heap id %d not registered"
456 };
457
458 /*!
459 * Error raised in a create call when a name fails to be added
460 * to the NameServer table. This can be because the name already
461 * exists, the table has reached its max length, or out of memory.
462 */
463 config Error.Id E_nameFailed = {
464 msg: "E_nameFailed: '%s' name failed to be added to NameServer"
465 };
466
467 /*!
468 * Trace setting
469 *
470 * This flag allows the configuration of the default module trace
471 * settings.
472 */
473 config Bool traceFlag = false;
474
475 /*!
476 * Number of heapIds in the system
477 *
478 * This allows MessageQ to pre-allocate the heaps table.
479 * The heaps table is used when registering heaps.
480 *
481 * There is no default heap, so unless the system is only using
482 * {@link #staticMsgInit}, the application must register a heap.
483 */
484 config UInt16 numHeaps = 8;
485
486 /*!
487 * Maximum number of MessageQs that can be dynamically created
488 */
489 config UInt maxRuntimeEntries = NameServer.ALLOWGROWTH;
490
491 /*!
492 * Gate used to make the name table thread safe
493 *
494 * This gate is used when accessing the name table during
495 * a {@link #create}, {@link #delete}, and {@link #open}.
496 *
497 * This gate is also used to protect MessageQ when growing
498 * internal tables in the {@link #create}.
499 *
500 * The table is in local memory, not shared memory. So a
501 * single processor gate will work.
502 *
503 * The default will be {@link xdc.runtime.knl.GateThread}
504 * instance.
505 */
506 config IGateProvider.Handle nameTableGate = null;
507
508 /*!
509 * Maximum length for Message queue names
510 */
511 config UInt maxNameLen = 32;
512
513 /*!
514 * Section name is used to place the names table
515 */
516 metaonly config String tableSection = null;
517
518 /*!
519 * ======== registerHeapMeta ========
520 * Statically register a heap with MessageQ
521 *
522 * Build error if heapId is in use.
523 *
524 * @param(heap) Heap to register
525 * @param(heapId) heapId associated with the heap
526 */
527 metaonly Void registerHeapMeta(IHeap.Handle heap, UInt16 heapId);
528
529 /*!
530 * ======== registerTransportMeta ========
531 * Statically register a transport with MessageQ
532 *
533 * Build error if remote processor already has a transport
534 * registered.
535 *
536 * @param(transport) transport to register
537 * @param(procId) procId that transport communicaties with
538 * @param(priority) priority of transport
539 */
540 metaonly Void registerTransportMeta(IMessageQTransport.Handle transport, UInt16 procId, UInt priority);
541
542 /*!
543 * ======== registerTransport ========
544 * Register a transport with MessageQ
545 *
546 * This API is called by the transport when it is created.
547 *
548 * @param(transport) transport to register
549 * @param(procId) MultiProc id that transport communicates with
550 * @param(priority) priority of transport
551 *
552 * @b(returns) Whether the register was successful.
553 */
554 Bool registerTransport(IMessageQTransport.Handle transport, UInt16 procId,
555 UInt priority);
556
557 /*!
558 * ======== unregisterTransport ========
559 * Unregister a transport with MessageQ
560 *
561 * @param(procId) unregister transport that communicates with
562 * this remote processor
563 * @param(priority) priority of transport
564 */
565 Void unregisterTransport(UInt16 procId, UInt priority);
566
567 instance:
568
569 /*!
570 * ISync handle used to signal IO completion
571 *
572 * The ISync instance is used in the {@link #get} and {@link #put}.
573 * The {@link xdc.runtime.knl.ISync#signal} is called as part
574 * of the {@link #put} call. The {@link xdc.runtime.knl.ISync#wait} is
575 * called in the {@link #get} if there are no messages present.
576 */
577 config ISync.Handle synchronizer = null;
578
579 /*! @_nodoc
580 * ======== create ========
581 * Create a message queue
582 *
583 * @param(name) Name of the message queue.
584 */
585 create(String name);
586
587 internal:
588 589 590 591 592 593 594 595 596 597 598
599
600 /*! Mask to extract version setting */
601 const UInt VERSIONMASK = 0xE000;
602
603 /*! Version setting */
604 const UInt HEADERVERSION = 0x2000;
605
606 /*! Mask to extract Trace setting */
607 const UInt TRACEMASK = 0x1000;
608
609 /*! Shift for Trace setting */
610 const UInt TRACESHIFT = 12;
611
612 /*!
613 * Mask to extract priority setting.
614 * This is needed here for ROV but must match
615 * the value defined in ti/ipc/MessageQ.h
616 */
617 const UInt PRIORITYMASK = 0x3;
618
619 /*! Mask to extract priority setting */
620 const UInt TRANSPORTPRIORITYMASK = 0x1;
621
622 /*! return code for Instance_init */
623 const Int PROXY_FAILURE = 1;
624
625 626 627 628
629 const UInt16 STATICMSG = 0xFFFF;
630
631 /*! Required first field in every message */
632 @Opaque struct MsgHeader {
633 Bits32 reserved0;
634 Bits32 reserved1;
635 Bits32 msgSize;
636 Bits16 flags;
637 Bits16 msgId;
638 Bits16 dstId;
639 Bits16 dstProc;
640 Bits16 replyId;
641 Bits16 replyProc;
642 Bits16 srcProc;
643 Bits16 heapId;
644 Bits16 seqNum;
645 Bits16 reserved;
646 };
647
648 struct HeapEntry {
649 IHeap.Handle heap;
650 UInt16 heapId;
651 };
652
653 struct TransportEntry {
654 IMessageQTransport.Handle transport;
655 UInt16 procId;
656 };
657
658 /*!
659 * ======== nameSrvPrms ========
660 * This Params object is used for temporary storage of the
661 * module wide parameters that are for setting the NameServer instance.
662 */
663 metaonly config NameServer.Params nameSrvPrms;
664
665 /*!
666 * Statically registered heaps
667 *
668 * This configuration parameter allows the static registeration
669 * of heaps. The index of the array corresponds to the heapId.
670 */
671 metaonly config HeapEntry staticHeaps[];
672
673 /*!
674 * Statically registered transports
675 *
676 * This configuration parameter allows the static registeration
677 * of transports. The index of the array corresponds to the procId.
678 */
679 metaonly config TransportEntry staticTransports[];
680
681 /*!
682 * Allows for the number of dynamically created message queues to grow.
683 */
684 UInt16 grow(Object *obj, Error.Block *eb);
685
686 struct Instance_State {
687 QueueId queue;
688 ISync.Handle synchronizer;
689 List.Object normalList;
690 List.Object highList;
691 Ptr nsKey;
692 SyncSem.Handle syncSemHandle;
693 Bool unblocked;
694 };
695
696 struct Module_State {
697 IMessageQTransport.Handle transports[][2];
698 Handle queues[];
699 IHeap.Handle heaps[];
700 IGateProvider.Handle gate;
701 UInt16 numQueues;
702 UInt16 numHeaps;
703 NameServer.Handle nameServer;
704 Bool canFreeQueues;
705 UInt16 seqNum;
706 };
707 }