为更好地发挥C6678的多核性能,需要用到多核协作。幸运的是,我们可以使用官方提供的IPC模块。
IPC=Inter-Processor Communication, 核间通信,粗略来说就是多核之间进行信息、数据交换。
作为入门篇,本文不打算深入讨论IPC,仅仅列出自带的两个简单示例:Notify和MessageQ.
“通知”(Notify)模型
“消息队列”(MessageQ)模型
以下介绍Notify示例的创建过程以及测试结果。
首先新建一个项目,取名demo_ipcNotify,项目类型从模板中选择
选择"IPC and I/O Examples"分支下的“C6678 Examples”
然后【Next】,在XDCtools version选择3.23.4.60(不带"core"后缀的那一个)
创建并编译链接无错误之后执行Debug
建议勾选下方的"Create a debug group for selected cores"
如果没有选,可以在稍后执行如下操作
分组的好处是,当有多个核心加载时,不必一一启动,只需要在组别上点击启动(分组下所有核心全部启动)
这样做虽然不是必要的,但建议这样做。
如果勾选了分组,将会是如下这个样子,测试的时候只需在"Group 1"上点击一次【Step On(继续将执行)】
以下是测试示例的输出(中间有部分省略)
[C66xx_6] main: MultiProc id = 6
main: MultiProc name = CORE6
[C66xx_7] main: MultiProc id = 7
main: MultiProc name = CORE7
[C66xx_0] main: MultiProc id = 0
[C66xx_1] main: MultiProc id = 1
[C66xx_2] main: MultiProc id = 2
[C66xx_3] main: MultiProc id = 3
[C66xx_4] main: MultiProc id = 4
[C66xx_5] main: MultiProc id = 5
[C66xx_0] main: MultiProc name = CORE0
[C66xx_1] main: MultiProc name = CORE1
[C66xx_2] main: MultiProc name = CORE2
[C66xx_3] main: MultiProc name = CORE3
[C66xx_4] main: MultiProc name = CORE4
[C66xx_5] main: MultiProc name = CORE5
[C66xx_0] tsk1_func: Sent request #0 to CORE1
[C66xx_1] tsk1_func: Received request #1 from CORE0
tsk1_func: Sent request #1 to CORE2
[C66xx_2] tsk1_func: Received request #1 from CORE1
tsk1_func: Sent request #1 to CORE3
[C66xx_3] tsk1_func: Received request #1 from CORE2
tsk1_func: Sent request #1 to CORE4
///省略///
[C66xx_3] tsk1_func: Received request #10 from CORE2
tsk1_func: Sent request #10 to CORE4
Test completed
[C66xx_4] tsk1_func: Received request #10 from CORE3
tsk1_func: Sent request #10 to CORE5
Test completed
[C66xx_5] tsk1_func: Received request #10 from CORE4
tsk1_func: Sent request #10 to CORE6
Test completed
[C66xx_6] tsk1_func: Received request #10 from CORE5
tsk1_func: Sent request #10 to CORE7
Test completed
[C66xx_7] tsk1_func: Received request #10 from CORE6
tsk1_func: Sent request #10 to CORE0
Test completed
[C66xx_0] tsk1_func: Received request #10 from CORE7
Test completed
类似的可以新建一个MessageQ示例项目
后续步骤同上,测试的输出如下(中间有部分省略)
[C66xx_1] Start the main loop
[C66xx_5] Start the main loop
[C66xx_7] Start the main loop
[C66xx_6] Start the main loop
[C66xx_0] Start the main loop
[C66xx_2] Start the main loop
[C66xx_3] Start the main loop
[C66xx_4] Start the main loop
[C66xx_0] Sending a message #1 to CORE1
[C66xx_1] Sending a message #1 to CORE2
[C66xx_2] Sending a message #1 to CORE3
[C66xx_3] Sending a message #1 to CORE4
[C66xx_4] Sending a message #1 to CORE5
[C66xx_5] Sending a message #1 to CORE6
[C66xx_6] Sending a message #1 to CORE7
[C66xx_7] Sending a message #1 to CORE0
///省略///
[C66xx_5] Sending a message #9 to CORE6
[C66xx_6] Sending a message #9 to CORE7
[C66xx_7] Sending a message #9 to CORE0
[C66xx_0] Sending a message #10 to CORE1
[C66xx_1] Sending a message #10 to CORE2
The test is complete
[C66xx_2] Sending a message #10 to CORE3
The test is complete
[C66xx_3] Sending a message #10 to CORE4
The test is complete
[C66xx_4] Sending a message #10 to CORE5
The test is complete
[C66xx_5] Sending a message #10 to CORE6
The test is complete
[C66xx_6] Sending a message #10 to CORE7
The test is complete
[C66xx_7] Sending a message #10 to CORE0
The test is complete
[C66xx_0] The test is complete
示例Notify的主要代码
#include
/* XDC.RUNTIME module Headers */
#include
/* IPC module Headers */
#include
#include
#include
/* BIOS6 module Headers */
#include
#include
#include
/* To get globals from .cfg Header */
#include
#define INTERRUPT_LINE 0
/* Notify event number that the app uses */
#define EVENTID 10
/* Number of times to run the loop */
#define NUMLOOPS 10
UInt32 seq = 0;
UInt16 recvProcId;
UInt16 srcProc, dstProc;
/*
* ======== cbFxn ========
* This function was registered with Notify. It is called when any event is
* sent to this processor.
*/
Void cbFxn(UInt16 procId, UInt16 lineId, UInt32 eventId, UArg arg,
UInt32 payload)
{
/* The payload is a sequence number. */
recvProcId = procId;
seq = payload;
Semaphore_post(semHandle);
}
/*
* ======== tsk0_func ========
* Sends an event to the next processor then pends on a semaphore.
* The semaphore is posted by the callback function.
*/
Void tsk0_func(UArg arg0, UArg arg1)
{
Int i = 1;
Int status;
if (MultiProc_self() == 0)
{
while (i <= NUMLOOPS)
{
/* Send an event to the next processor */
status = Notify_sendEvent(dstProc, INTERRUPT_LINE, EVENTID, i,
TRUE);
/* Continue until remote side is up */
if (status < 0)
{
continue;
}
System_printf("tsk1_func: Sent request #%d to %s\n", seq,
MultiProc_getName(dstProc));
/* Wait to be released by the cbFxn posting the semaphore */
Semaphore_pend(semHandle, BIOS_WAIT_FOREVER);
System_printf("tsk1_func: Received request #%d from %s\n", seq,
MultiProc_getName(recvProcId));
/* increment for next iteration */
i++;
}
}
else
{
while (seq < NUMLOOPS)
{
/* wait forever on a semaphore, semaphore is posted in callback */
Semaphore_pend(semHandle, BIOS_WAIT_FOREVER);
System_printf("tsk1_func: Received request #%d from %s\n", seq,
MultiProc_getName(recvProcId));
/* Send an event to the next processor */
status = Notify_sendEvent(dstProc, INTERRUPT_LINE, EVENTID, seq,
TRUE);
if (status < 0)
{
System_abort("sendEvent failed\n");
}
System_printf("tsk1_func: Sent request #%d to %s\n", seq,
MultiProc_getName(dstProc));
}
}
System_printf("Test completed\n");
BIOS_exit(0);
}
/*
* ======== main ========
* Synchronizes all processors (in Ipc_start), calls BIOS_start, and registers
* for an incoming event
*/
Int main(Int argc, Char* argv[])
{
Int status;
UInt numProcs = MultiProc_getNumProcessors();
/*
* Determine which processors Notify will communicate with based on the
* local MultiProc id. Also, create a processor-specific Task.
*/
srcProc = ((MultiProc_self() - 1 + numProcs) % numProcs);
dstProc = ((MultiProc_self() + 1) % numProcs);
System_printf("main: MultiProc id = %d\n", MultiProc_self());
System_printf("main: MultiProc name = %s\n",
MultiProc_getName(MultiProc_self()));
/*
* Ipc_start() calls Ipc_attach() to synchronize all remote processors
* because 'Ipc.procSync' is set to 'Ipc.ProcSync_ALL' in *.cfg
*/
status = Ipc_start();
if (status < 0)
{
System_abort("Ipc_start failed\n");
}
/*
* 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) cbFxn, NULL);
if (status < 0)
{
System_abort("Notify_registerEvent failed\n");
}
BIOS_start();
return (0);
}
#include
#include
/* XDC.RUNTIME module Headers */
#include
#include
/* IPC module Headers */
#include
#include
#include
#include
/* BIOS6 module Headers */
#include
#include
/* To get globals from .cfg Header */
#include
#define HEAP_NAME "myHeapBuf"
#define HEAPID 0
#define NUMLOOPS 10
Char localQueueName[10];
Char nextQueueName[10];
UInt16 nextProcId;
/*
* ======== tsk0_func ========
* Allocates a message and ping-pongs the message around the processors.
* A local message queue is created and a remote message queue is opened.
* Messages are sent to the remote message queue and retrieved from the
* local MessageQ.
*/
Void tsk0_func(UArg arg0, UArg arg1)
{
MessageQ_Msg msg;
MessageQ_Handle messageQ;
MessageQ_QueueId remoteQueueId;
Int status;
UInt16 msgId = 0;
HeapBufMP_Handle heapHandle;
HeapBufMP_Params heapBufParams;
if (MultiProc_self() == 0)
{
/*
* Create the heap that will be used to allocate messages.
*/
HeapBufMP_Params_init(&heapBufParams);
heapBufParams.regionId = 0;
heapBufParams.name = HEAP_NAME;
heapBufParams.numBlocks = 1;
heapBufParams.blockSize = sizeof(MessageQ_MsgHeader);
heapHandle = HeapBufMP_create(&heapBufParams);
if (heapHandle == NULL)
{
System_abort("HeapBufMP_create failed\n");
}
}
else
{
/* Open the heap created by the other processor. Loop until opened. */
do
{
status = HeapBufMP_open(HEAP_NAME, &heapHandle);
/*
* Sleep for 1 clock tick to avoid inundating remote processor
* with interrupts if open failed
*/
if (status < 0)
{
Task_sleep(1);
}
} while (status < 0);
}
/* Register this heap with MessageQ */
MessageQ_registerHeap((IHeap_Handle) heapHandle, 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(nextQueueName, &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);
if (MultiProc_self() == 0)
{
/* Allocate a message to be ping-ponged around the processors */
msg = MessageQ_alloc(HEAPID, sizeof(MessageQ_MsgHeader));
if (msg == NULL)
{
System_abort("MessageQ_alloc failed\n");
}
/*
* Send the message to the next processor and wait for a message
* from the previous processor.
*/
System_printf("Start the main loop\n");
while (msgId < NUMLOOPS)
{
/* Increment...the remote side will check this */
msgId++;
MessageQ_setMsgId(msg, msgId);
System_printf("Sending a message #%d to %s\n", msgId,nextQueueName);
/* send the message to the remote processor */
status = MessageQ_put(remoteQueueId, msg);
if (status < 0)
{
System_abort("MessageQ_put had a failure/error\n");
}
/* Get a message */
status = MessageQ_get(messageQ, &msg, MessageQ_FOREVER);
if (status < 0)
{
System_abort("This should not happen since timeout is forever\n");
}
}
}
else
{
/*
* Wait for a message from the previous processor and
* send it to the next processor
*/
System_printf("Start the main loop\n");
while (TRUE)
{
/* 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("Sending a message #%d to %s\n",MessageQ_getMsgId(msg),
nextQueueName);
/* Get the message id */
msgId = MessageQ_getMsgId(msg);
/* send the message to the remote processor */
status = MessageQ_put(remoteQueueId, msg);
if (status < 0)
{
System_abort("MessageQ_put had a failure/error\n");
}
/* test done */
if (msgId >= NUMLOOPS)
{
break;
}
}
}
System_printf("The test is complete\n");
BIOS_exit(0);
}
/*
* ======== main ========
* Synchronizes all processors (in Ipc_start) and calls BIOS_start
*/
Int main(Int argc, Char* argv[])
{
Int status;
nextProcId = (MultiProc_self() + 1) % MultiProc_getNumProcessors();
/* Generate queue names based on own proc ID and total number of procs */
System_sprintf(localQueueName, "%s", MultiProc_getName(MultiProc_self()));
System_sprintf(nextQueueName, "%s", MultiProc_getName(nextProcId));
/*
* Ipc_start() calls Ipc_attach() to synchronize all remote processors
* because 'Ipc.procSync' is set to 'Ipc.ProcSync_ALL' in *.cfg
*/
status = Ipc_start();
if (status < 0)
{
System_abort("Ipc_start failed\n");
}
BIOS_start();
return (0);
}