导入SharedRegion模块
SharedRegion模块是一个共享区域,特别是对于多处理器环境下,SharedRegion模块就是用于让一个内存区域能被不同处理器共享并操作。这个模块会给每个处理器上创建一个共享内存区域查找表,这个查找表保证各个处理器能查看到系统内的所有共享区域。查找表中共享内存区域在所有查找表中的区域ID是一致的,在运行时,查找表可以通过共享区域ID及共享区域名称来快速查找共享区域。
添加查找表
添加查找表实际上是指在查找表中添加共享区域的入口(Entry),在导入SharedRegion模块后,默认就已经添加了区域ID为0的SharedRegion,这个ID为0的共享区域是可以被所有处理器所共享的。查找表的添加有两种方式,一种是静态添加,这种方式下添加共享区域一般都所有核共享的,另一种是动态添加,这种方式可以添加一些只能部分核有效的共享区域。
a). 静态添加
静态添加主要是在.cfg文件中完成,在调用Ipc_start()时,完成SharedRegion模块的配置。以下代码都是直接在.cfg文件中添加的首先导入SharedRegion模块,完成全局设置,首先SharedRegion.numEntries来设置系统中最大共享区域的数目。如果所有共享内存区域在所有处理器中都有相同基地址时,translate选项将会被设置为false意思是不用进行地址转换,而如果设置为true,则需要做地址转换。(本节的例程只需要用到一个共享区域,所以这两个选项其实都可以不用设置,默认就好了)。
var SharedRegion = xdc.useMoudle(ti.sdo.ipc.SharedRegion);
SharedReginon.numEntries = 4; //最大共享区域数目
SharedReginon。translate = true; //是否需要进行地址转换
修改后的.cfg:
这里的Entry参数含义如下:
1)base:区域的基地址,不同处理器其基地址可以是不同的。
2)len:区域的长度,同一个共享区域在所有处理器的查找表中的长度应该是相同的。
3)ownerProcID:管理该区域的处理器ID,如果存在区域所有者,这个区域所有者(owner)就是创造HeapMemMp实例的,而其他核打开这个实例。
4)isValid:表明该区域在当前核上是否可用,判断当前核能否使用此共享区域的。
5)cacheLineSize:这个值在所有核的查找表中都应该是相同的
6)createHeap:表明是否需要给当前区域创建一个堆。
7)name:区域的名称。
一个查找表中最大进入数目通常使用ShareRegion.numEntries中静态设置。因为每个处理器都存储着SharedRegion查找表中共享内存区域的信息,所以当在一个查找表中增加或删除共享区域入口时,都必须更新所有其他查找表中的数据,查找表中的入口越多,那么查找表的查找时间就越长。
配置好的.cfg文件:
/*
* Copyright (c) 2012, Texas Instruments Incorporated
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of Texas Instruments Incorporated nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* */
var MultiProc = xdc.useModule('ti.sdo.utils.MultiProc');
/*
* Get the list of names that the build device supports.
* I.e. ["CORE0", "CORE1", "CORE2" ... ]
*/
var nameList = MultiProc.getDeviceProcNames();
/*
* Since this is a single-image example, we don't (at build-time) which
* processor we're building for. We therefore supply 'null'
* as the local procName and use MultiProc_setLocalId to set the procId
* at runtime.
*/
MultiProc.setConfig(null, nameList);
var System = xdc.useModule('xdc.runtime.System');
var SysStd = xdc.useModule('xdc.runtime.SysStd');
System.SupportProxy = SysStd;
/* Modules explicitly used in the application */
var Notify = xdc.useModule('ti.sdo.ipc.Notify');
var MessageQ = xdc.useModule('ti.sdo.ipc.MessageQ');
var HeapBufMP = xdc.useModule('ti.sdo.ipc.heaps.HeapBufMP');
var Ipc = xdc.useModule('ti.sdo.ipc.Ipc');
var BIOS = xdc.useModule('ti.sysbios.BIOS');
BIOS.heapSize = 0x8000;
var Task = xdc.useModule('ti.sysbios.knl.Task');
var tsk0 = Task.create('&tsk0_func');
tsk0.instance.name = "tsk0";
/* To avoid wasting shared memory for MessageQ transports */
for (var i = 0; i < MultiProc.numProcessors; i++) {
Ipc.setEntryMeta({
remoteProcId: i,
setupMessageQ: true,
});
}
/* Synchronize all processors (this will be done in Ipc_start) */
Ipc.procSync = Ipc.ProcSync_ALL;
/*
* Need to define the shared region. The IPC modules use this
* to make portable pointers. All processors need to add this
* call with their base address of the shared memory region.
* If the processor cannot access the memory, do not add it.
*/
var SharedRegion = xdc.useModule('ti.sdo.ipc.SharedRegion');
/* Shared Memory base address and length */
var SHAREDMEM_0 = 0x0c000000;
var SHAREDMEMSIZE_0 = 0x00800000;
SharedRegion.setEntryMeta(0, // 区域ID
{ base: SHAREDMEM_0, // 基地址
len: SHAREDMEMSIZE_0, // 区域大小
ownerProcId: 0, // 所有者的核ID
isValid: true, // 对于当前核,该区域是否有效
name: "DDR2_RAM_0", // 区域名称
});
/* Create a semaphore with count 0 */
var Semaphore = xdc.useModule('ti.sysbios.knl.Semaphore');
Program.global.semHandle = Semaphore.create(0);
/*
* @(#) ti.sdo.ipc.examples.multicore.evm667x; 1, 0, 0, 0,1; 5-22-2012 16:36:06; /db/vtree/library/trees/ipc/ipc-h32/src/ xlibrary
*/
var execontextInstance0 = xdc.lookup('xdc.platform.ExeContext.Instance#0');
execontextInstance0.memoryMap["MSMCSRAM"].len = 16777216;
b). 动态添加
一般少见,动态添加主要通过SharedRegion_setEntry()函数来设置如下入口参数。
typedef struct SharedRegion_Entry{
Ptr base;
SizeT len;
UInt16 ownerProcId;
Bool isValid;
Bool cacheLineSize;
Bool createHeap;
String name;
}SharedRegion_Entry
使用共享区域内存
其一般性的使用方法:
heap = (IHeap_Handle)SharedRegion_getHeap(regionId); // 通过区域ID获得共享区域的堆句柄
buf = Memory_alloc(heap, size, align, NULL); // 通过堆分配区域内存
参数:
1)regionId: 是区域ID,在静态添加中.cfg文件中通过SharedRegion.setEntryMeta中设置的,如果不知道,可以通过SharedRegion_getId()根据特定的地址来获得当前区域Id,或者通过SharedRegion_getIdByName()根据区域的名称来获得区域Id。
2)buf: 分配的内存缓冲区指针,
3)heap: 可用于分配内存的堆句柄
4)size: 分配的内存缓冲区大小
5)align: 对齐参数
添加相应头文件:
#include <xdc/runtime/Memory.h>
#include <ti/ipc/SharedRegion.h>
地址转换
在一个拥有共享内存区域的环境中,一个主要的问题是这些共享区域在不同处理器上可能会被映射到不同地址空间,即同样一块共享内存区域在不同处理的本地地址空间的逻辑位置是不同,因此此时就需要在不同处理器之间转换的地址指针。先前在静态设置中将SharedRegion.translate选项设置为true,则需要做地址转换。而设置为false时,不同处理器上同一样的内存缓冲区其对应地址都是相同,所以不需要做地址转换。这里为了描述共享内存间的地址转换过程,我们将其设置为true(默认)。
首先需要介绍两个概念:
共享区域地址SRPtr:共享区域地址是当前内存缓冲区在共享区域SharedRegion上的地址,可以视为全局地址,指向同一内存的SRPtr地址在所有处理器上都是相同,所以我们给其他核传递的就是这个地址。共享区域指针(SRPtr)是一个32位指针,其由一个ID和其偏移组成。一个SRPtr的最重要的位是用于表明ID号的,而ID号表明当前区域入口在查找表中的位置。
本地地址addr:共享区域地址SRPtr是不能在本地使用,一般情况下,需要转换成本地地址才能被本地处理器使用。
关于地址转换的几个函数:
SharedRegion_getPtr()根据共享区域指针来获得本地指针
SharedRegion_getSRPtr()根据给定的本地指针及区域ID来获得当前共享区域指针
缓存Cache问题
主核在往内存写数据时,很有可能只是将数据写Cache中,还没有写入到内存中去,所以这个时候,从核肯定不能从内存中读到写入数据。解决方法是将回写Cache。具体步骤:
a). 导入头文件
#include<ti/sysbios/hal/Cache.h>
b). 给内存写入数据后,回写Cache
Void Cache_wb(Ptr blockPtr, SizeT byteCnt, Bits16 type, Bool wait);
blockPrt是指内存地址,byteCnt指回写数据大小,type指缓存类型,wait指是否等待。回写全部Cache可以调用Void Cache_wbAll();
c). 在从内存读出数据前,先让Cache失效
Void Cache_disable(Bits16 type);
type指缓存类型,这里可以选择Cache_Type_ALL,即全部缓存。
代码:
#include<xdc/std.h>
#include<stdio.h>
#include<stdlib.h>
/* -----------------------------------XDC.RUNTIME module Headers */
#include <xdc/runtime/System.h>
#include <ti/sysbios/hal/Cache.h>
/* ----------------------------------- IPC module Headers */
#include <ti/ipc/MultiProc.h>
#include <ti/ipc/Notify.h>
#include <xdc/runtime/IHeap.h>
#include <ti/ipc/Ipc.h>
/* ----------------------------------- BIOS6 module Headers */
#include <ti/sysbios/knl/Semaphore.h>
#include <ti/sysbios/knl/Task.h>
#include <ti/sysbios/BIOS.h>
/* ----------------------------------- To get globals from .cfg Header */
#include <xdc/cfg/global.h>
#include <xdc/runtime/Memory.h>
#include <ti/ipc/SharedRegion.h>
#define INTERRUPT_LINE 0
#define BufEVENT 10
#define dateNum 8 //76800
unsigned char *inBuf=NULL;
SharedRegion_SRPtr inBuf_srptr;
/*
* ======== cbFxn ========
* 这是Notify模块的注册函数
* procId表示激动注册函数的核ID,或者说该事件是从哪个核来的
*/
Void cbFxn(UInt16 procId, UInt16 lineId,
UInt32 eventId, UArg arg, UInt32 payload)
{
inBuf_srptr=(SharedRegion_SRPtr)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 status;
Int i;
int coreId=MultiProc_self();
// 分配内存
if (coreId == 0) {
inBuf = (unsigned char*)Memory_alloc(SharedRegion_getHeap(0), dateNum, 128, NULL);
if(inBuf==NULL)
{
System_printf("malloc Buf failed
");
BIOS_exit(0);
}
for(i=0;i<dateNum;i++){ // 写入数据
inBuf[i]=i*3+5;
}
inBuf_srptr = SharedRegion_getSRPtr(inBuf, 0);
System_printf("inBuf address 0x%x
",inBuf);
System_printf("outBuf date is ");
for(i=0;i<dateNum;i++)
System_printf("%d ",inBuf[i]);
System_printf("
");
Cache_wbAll();// Write back all caches();
for(i=1;i<8;i++)
status = Notify_sendEvent(i, INTERRUPT_LINE, BufEVENT, (UInt32)inBuf_srptr, TRUE);
}else{
Semaphore_pend(semHandle, BIOS_WAIT_FOREVER); // 等待从核完成全部任务
Cache_disable(Cache_Type_ALL);
inBuf=SharedRegion_getPtr(inBuf_srptr);
System_printf("inBuf address 0x%x
",inBuf);
System_printf("regionId is %d
",SharedRegion_getId(inBuf)); // 打印当前共享区域ID
System_printf("outBuf date is ");
for(i=0;i<dateNum;i++)
System_printf("%d ",inBuf[i]);
System_printf("
");
}
// 各核完成任务后可以退出了
System_printf("SharedMem is finished
");
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;
/*
* 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
");
}
if (MultiProc_self() == 0) {
Int i;
for(i=1;i<8;i++)
status = Notify_registerEvent(i, INTERRUPT_LINE, BufEVENT,
(Notify_FnNotifyCbck)cbFxn, NULL);
}else{
// 从核完成事件注册
status = Notify_registerEvent(0, INTERRUPT_LINE, BufEVENT,
(Notify_FnNotifyCbck)cbFxn, NULL);
}
BIOS_start();
return (0);
}
输出结果:
通过上面结果,可以看出核1-7完美读取了核0写入共享内存的数据。
需要注意的是有些情况下,即使在需要地址转换的情况下,各处理器的本地地址也有可能是相同的,就像本节的例子,但是当共享内存区域的数据成份比较复杂时,同一区域在不同处理器的本地地址就很可能不同了,所以地址转换是非常有必要的。