Hi Jodah,
I would like to manage my application on Concerto the following way:
I'm using messageQ and Notify module of IPC.
The scenarion of my application shall go like this:
Two cores M3 and C28.....
I don't want it be managed as polling.
I would like that each core will get messages out of the qeue on interrupt when another core calls MessageQ_put
For urgent message such as inform another core for error on another core, I'll use Notify_sendEvent(dstProc, INTERRUPT_LINE, EVENTID, payload, TRUE);
Jodah I have two questions:
1. Notify_sendEvent- Can it be called from HWI? The TRUE flag .....how shall my application behave if Notify_sendEvent suqentually one after another without another core got previous event.
I've implemented Notify function as following, please review
void SendNotifyEvent (UInt32 payload){ Int status;
// Send an event to the remote processor status = Notify_sendEvent(dstProc, INTERRUPT_LINE, EVENTID, payload, TRUE); if (status < 0) { System_abort("sendEvent failed\n"); }}
what shall be application behaviour if status is Notify_E_TIMEOUT......specially if SendNotifyEvent () is called from HWI ?
2. How to manage MessageQ_put as interrupt driven.....
Please confirm that my implementation today does it according to the bellow code:
.cfg file IPC configuration is as following:
var MessageQ = xdc.useModule('ti.sdo.ipc.MessageQ');var Notify = xdc.useModule('ti.sdo.ipc.Notify');
BIOS.heapSize = 0x4000;
/* IPC *//* Use the f28m35x IpcMgr to configure the shared buffers used by IPC */var IpcMgr = xdc.useModule('ti.sdo.ipc.family.f28m35x.IpcMgr');IpcMgr.readAddr = 0x20016000;IpcMgr.writeAddr = 0x20014000;
-----------------------------------------------------
Initialization of MessageQ in the code:
void IPC_init (void){ Int status; UInt numProcs = MultiProc_getNumProcessors(); // Number of processors, in the Concerto case = 2
// Generate queue names based on own proc ID and total number of procs System_sprintf(localQueueName, "%s_queue", MultiProc_getName(MultiProc_self())); System_sprintf(remoteQueueName, "%s_queue", MultiProc_getName(1 - MultiProc_self()));
// Determine which processors Notify will communicate with based on the local MultiProc id. srcProc = ((MultiProc_self() - 1 + numProcs) % numProcs); dstProc = ((MultiProc_self() + 1) % numProcs);
// Register call back with Notify. It will be called when the processor // with id = srcProc sends event number EVENTID to this processor. status = Notify_registerEvent(srcProc, INTERRUPT_LINE, EVENTID, (Notify_FnNotifyCbck)NotifycallBackFxn, NULL); if (status < 0) { System_abort("Notify_registerEvent failed\n"); }
// Register default system heap with MessageQ MessageQ_registerHeap((IHeap_Handle)(MsgQHeapBuf0), HEAPID);
// Create the local message queue messageQ = MessageQ_create(localQueueName, NULL); if (messageQ == NULL) { System_abort("MessageQ_create failed\n" ); }
// Open the remote message queue. Spin until it is ready. do { status = MessageQ_open(remoteQueueName, &remoteQueueId); // Sleep for 1 clock tick to avoid inundating remote processor // with interrupts if open failed if (status < 0) { Task_sleep(1); } } while (status < 0);
}
////Add message to Qeue
void IPC_sendMsg ( MY_M3_C28_Msg *Message ){ Int status;
// Increment the msgID and set it msgId++; MessageQ_setMsgId(Message, msgId);
System_printf("Sending a message #%d to %s\n", msgId, remoteQueueName);
// send the message to the remote processor status = MessageQ_put(remoteQueueId, (MessageQ_Msg)Message); if (status < 0) { System_abort("MessageQ_put had a failure/error\n"); }
Get Message from the qeue:
MY_M3_C28_Msg * IPC_recMsg (void){ Int status; MessageQ_Msg msg;
// Get a message status = MessageQ_get(messageQ, &msg, MessageQ_FOREVER); if (status < 0) { System_abort("This should not happen since timeout is forever\n"); }
System_printf("Received message #%d from %s\n", MessageQ_getMsgId(msg), remoteQueueName); return ((MY_M3_C28_Msg *)msg);}
Void IPC_Task(UArg arg0, UArg arg1){
// Init the IPC, M3 <-> C28x communication IPC_init ();
// Check the WD timer "0" interrupt while (1) { // receive a message myRcvMessage = IPC_recMsg ();
switch (myRcvMessage->MsgType) { case 1: Semaphore_post(SEM_BB_WaitOnDataReceived); break;
default: break; }
//Free the received message IPC_freeMsg (myRcvMessage); }
How shall I change my code for avoid polling on getting messages from the qeue ? I know that messageQ can be managed not as polling, please guide what shall I add to my code to do so.....
3. Can MessageQ_put, MessageQ_get be called from HWI, SWI?
Thank you, Alla
1. Yes, you can call Notify_sendEvent from a Hwi. For Concerto, if there is no slot available and you have waitClear set to TRUE, it spins waiting for a slot to become free.
2. I would use the dedicate MTOC and CTOM memory for the IpcMgr.readAddr and IpcMgr.writeAddr. I scanned the code and it looks like you have the correct idea.
3. Yes both can be called from a Hwi or Swi. However, for the MessageQ_get, make sure you use a non-blocking synchronizer when you create the queue.
Todd
Hi Todd,
Thank you for reply.......
1. I don't understand If Notify_sendEvent can be called from HWI. Usually in HWI not accaptable to use loop (spinning waiting for a slot to become free.).....so how do you suggest to handle it?
2. Do you mean to map the shared memory to MTOC and CTOM?
3. Can you please guide me exactly how to define it .....what exactly changes in the code shall I do in :
and what exactly changes shall I do in:
thank you
A
Alla Shnaider1. I don't understand If Notify_sendEvent can be called from HWI. Usually in HWI not accaptable to use loop (spinning waiting for a slot to become free.).....so how do you suggest to handle it?
It is generally not recommended to spin in an Hwi because of the latency impact. However that is a decision you need to make. Note: you cannot call a blocking API in a Hwi (e.g. Semphore_pend with a non-zero timeout).
Alla Shnaider2. Do you mean to map the shared memory to MTOC and CTOM?
On the M3 side do:
var IpcMgr = xdc.useModule('ti.sdo.ipc.family.f28m35x.IpcMgr');IpcMgr.readAddr = 0x2007F000;IpcMgr.writeAddr = 0x2007F800;
On the X28 side do:
var IpcMgr = xdc.useModule('ti.sdo.ipc.family.f28m35x.IpcMgr');IpcMgr.readAddr = 0x3FC00;IpcMgr.writeAddr = 0x3F800;
Alla Shnaider3. Can you please guide me exactly how to define it .....what exactly changes in the code shall I do in :
ROV is a useful tool to help debug the issue. Other suggestion is to get MessageQ working on one core (easier to debug) and then move the functionality around as needed.
Thank you for reply.....
I have two questions to make sure that I've understand properly........
Please clarify.........
Regarding CTOM and MTOM....wanted to clarify
I'm managing MessageQ when writer does MessageQ_alloc and....... reader does MessageQ_free((MessageQ_Msg)Message);....freeing memoty....... after reading the message from the queue.....(meaning that for reader side its not only "read only" operation) ...
Ttherfore I would like to understand
if I'll use CTOM and MTOM shared region......if reader processor can do :
1. MessageQ_get >> 2. MessageQ_free
3. MessageQ_open
Can I still use CTOM and MTOC?...... or shall i better use S7-S8?
Additional question:
I'm using HeapBuf for IPC message allocation.......
MessageQ_alloc(HEAPID, sizeof(MY_M3_C28_Msg));....... when HEAPID is my Heap Buffer ID
I'm looking where actually shall I map HeapBuffer (used for IPC) to shared memory.....
My question : Is it registration of heap.... MessageQ_registerHeap((IHeap_Handle)(MsgQHeapBuf0), HEAPID);........actually does mapping of heap to the shared region...say to MTOC?..........
Thank you,
Alla
Alla,
For Concerto, this is how a message is passed from one core to another (pseudo-code).
Assume we are sending the message from the M3 to the C28 and the queue on the C28 has been created on the C28 and opened on the M3. Also assume both sides have a heap registered with MessageQ and the heapId is HEAPID. Also assume we are using MTOC and CTOM.
M3 sidemsg = MessageQ_alloc(HEAPID, size);
//fill in msg...
MessageQ_put(remoteQ, msg);
//MessageQ sees that this message is for the C28. So it copies the contents on the message into the MTOC memory and sends an interrupt to the C28. It then calls MessageQ_free on the msg...returning it to the heap.
C28 SideWhen the C28 interrupt runs, it posts a Swi. This Swi looks the msg in the MTOC memory. The Swi notes the size and the heapId of the msg. It calls MessageQ_alloc(heapId, size). It copies the contents of the MTOC msg into the newly allocated message. It then calls MessageQ_put of the message (note it gets the destination queue from the message). The Swi then updates its shared memory to let the M3 that the message has been processed.
When the application gets the message (via MessageQ_get), it can call MessageQ_free which returns it back to the heap.
The reverse (C28 to M3) is exactly the same except the CTOM memory would be used.
Observations
The registered heaps do not need to be shared memory. However, the heaps that are registered on both sides with the same heapId need to be similar. For example, if you allocate a message from the M3 with a size of 4096 bytes. The heap on the C28 must be able to honor an alloc for 4096 bytes since MessageQ will be internally doing this.
For Concerto, this approach is same regardless of which memory you give to the IpcMgr. Please note: other devices support a zero-copy transfer between cores because they have multi-core synchronization mechanisms (HW semaphores, spin-locks, Peterson algorithm, etc.) and have shared memory that all sides can read and write.
Hi Jodah and Todd,
Thank you for reply.
Currently I have a new problem. I manage 2 different message queus. One for managing messages received from PC (ARM<->PC) and another for handling messages from other controller (ARM<->Peripherial controller)
On DSP side I have two different threads(taks) with same priority. Each listens to coming messages from different message queue.
each thread does the following:
Task 1:
while (1) { // receive a message myRcvMessage = IPC_recMsg1 ();
m_pPCInterface->PCMessageHandle(myRcvMessage->MsgType, myRcvMessage->Data);
/////////////////////////////////////////////////////
Task 2:
while (1) { // receive a message myRcvMessage = IPC_recMsg2 ();
m_pPeriphContollerInterface->PeriphContollerMessageHandle(myRcvMessage->MsgType, myRcvMessage->Data);
IPC_recMsg1 uses "status = MessageQ_get(messageQ1, &msg, MessageQ_FOREVER);"
IPC_recMsg2 uses "status = MessageQ_get(messageQ2, &msg, MessageQ_FOREVER);"
the problem is that one tasks blocks another one. And I see that only one task listens to received messages.
In one of your replys you've mentioned that I have to use MessageQ_get not as blocking. Create MessageQ not as blocking.. (I'm not sure that I understand you clearly and how to do it)...I guess may be this the problem because I'm using:
// Create the local message queue messageQ = MessageQ_create(localQueueName, NULL);
Can you please advice, guide me stepby step how shall I create non blocking queus. I would like to manage each queu in different task , theefore both tasks shall be enabled to listen for incoming messages fro its own queue.
thank you,
Hi Alla,
Please, correct me if I'm wrong but I think that you can't have two active threads at the same time in the same core.
You can go from one to the other and go back and so on, but if both have a while (1) { } it will stay there until a higher priority thread preempts it or there's a termination condition. I think that this is your problem. Maybe using semaphores could "unblock" the tasks.
About the blocking condition of MessageQ I think that what they mean is: imagine you have a MessageQ_get with a very long timeout value and this MessageQ_get is inside a Hwi (it has high priority), this way you will block your system until the MessageQ_get returns, what might never happen.
Regards
Johannes
Hi Johannes,
Please correct me if I wrong is it blocking on MessageQ_get is as blocking on semaphore:
MessageQ_get(messageQ, &msg, MessageQ_FOREVER);
I have two different queus. Handling messages from both queus shall be handled by application in the same priority. I though using different tasks, which each one listens for received messages via MessageQ_get(messageQ, &msg, MessageQ_FOREVER);
How you suggest tohandle it?
thank you in advance
I'm speaking to your second question in this post about 2 threads waiting on 2 different MessageQs.
If you have a non-blocking Synchronizer then you must be spinning until the first thread gets a message? After it gets the message it will simply wait for another message because how is it going to yield the processor? That's the reason why in most cases when a MessgeQ_get is called from a Task, We use SyncSem as the synchronizer so the Task would block and yield the processor to another Task.
The other way to do this would be to call Task_yield() or Task_sleep() after the Task gets a message to allow the other Task a chance to execute.
Judah
If my reply answers your question please mark the thread as answered
Hi Judah,
"We use SyncSem as the synchronizer so the Task would block and yeild the processor if a message is not available......"
Please point me to example using SyncSem as the synchronizer so the Task would block and yield the processor to another Task. Do I have for each queus to use its own SyncSem as the synchronizer.
Please guide me how.
Thank you
The default if you don't specify anything when creating a MessageQ is to use SyncSem as the synchronizer. Each MessageQ instance would get its own synchronizer object. So if you do MessageQ_create("Name", NULL), by default your synchronizer is a SyncSem.
Here is an example of explicitly specifying SyncSem:
/* Create a message queue. Using SyncSem as the synchronizer */ syncSemHandle = SyncSem_create(NULL, NULL); MessageQ_Params_init(&messageQParams); messageQParams.synchronizer = SyncSem_Handle_upCast(syncSemHandle); messageQ = MessageQ_create(CORE0_MESSAGEQNAME, &messageQParams); if (messageQ == NULL) { System_abort("MessageQ_create failed\n" ); }
Here is an example of specifying SyncSwi which is non-blocking:
Swi_Params_init(&swiParams); swiParams.priority = 1; swiHandle = Swi_create(swi1_func, &swiParams, NULL); /* Create a message queue. Using SyncSwi as the synchronizer */ SyncSwi_Params_init(&syncSwiParams); syncSwiParams.swi = swiHandle; syncSwiHandle = SyncSwi_create(&syncSwiParams, NULL);
MessageQ_Params_init(&messageQParams); messageQParams.synchronizer = SyncSwi_Handle_upCast(syncSwiHandle); messageQ = MessageQ_create(CORE0_MESSAGEQNAME, &messageQParams); if (messageQ == NULL) { System_abort("MessageQ_create failed\n" ); }
Please advice for correct management IPC and Etherenet. I'm using messageQ via IPC and Etherent which is much faster then IPC messageQ.
I find my self to end of heap buffer (no free heap buffers) used for message allocation fail (even when there are used 4 messages in messageQ). Please advice for mutual management. How can I achieve the same (or almost the same) speed for passing received message from Ethernet and move it via IPC from M3 to C28?
I'm limmited in resorces (RAM)
My message size is 100 bytes. I've allocated HeapBuffer. I've defined on both cores:
MsgQHeapBuf0, block size 256, number of blocks 4, aligment 8 ,
MessageQSize 256, NumMessageQMessage 4
What is optimal size of HepBuf shall be?
I think this thread is marked answer so you aren't going to get any new response.
My recommendation would be to post your question on a new thread.
I'm don't know this for sure but I think TI has an MCUSDK which may have some examples using ethernet along with IPC on the Concerto board.