用于多核DSP开发的核间通信

article/2025/10/1 19:42:32

  TI的多核DSP以TMS320C6678为例,它是多核同构的处理器,内部是8个相同的C66x CorePac。对其中任意一个核的开发就和单核DSP开发的流程是类似的。
  但是如果仅仅只是每个核独立开发,那么很难体现出多核的优势。要充分发挥多核处理器的性能,势必需要涉及核间通信,另外还有许多共享资源的分配的问题需要考虑。
  IPC(Inter-Processor Communication)是RTSC体系下的一个Package,专门用来实现不同核之间的通信。IPC可以在这里下载。3.0版本以后的IPC会直接包含在对应的SDK里面,如果要用更早的版本,也可以再下载,只是要注意最好对上XDCTools和SYS/BIOS的版本。

IPC 版本3.50.041.25.03
XDCTools3.55.023.24.05
SYS/BIOS6.76.036.34.04

  3.50版本的IPC是包含在C667x的PDK里的,但我在安装的时候XDCTools安装失败了,可能是CCS版本比较老的原因,所以我就用的是1.25版本的IPC。虽然这两个版本号有很大差别,但是用起来应该差不多吧,我猜。这个Package的使用主要就看文档就好了,我在这里只做一些简短的记录。
  IPC Package里有很多module,要使用这些module,需要include的头文件在<ipc_install_dir>/packages/ti/ipc/目录下面,一般都会用到这些:

// type define
#include <xdc/std.h>#include <ti/ipc/Ipc.h>
#include <ti/ipc/MultiProc.h>
#include <ti/ipc/SharedRegion.h>
#include <ti/ipc/Notify.h>
#include <ti/ipc/MessageQ.h>
#include <ti/ipc/GateMP.h>

  不管是用Linux内核还是用SYS/BIOS内核,用到的都是这些头文件。在include这些头文件之前要另外include一个关于类型定义的头文件<xdc/std.h>。关于这些头文件里的API的说明在<ipc_install_dir>/docs/doxygen/html/index.html里有,是用doxygen根据源码里的注释生成的文档。
  由XDCTools编译生成的<ipc_install_dir>/packages/ti/sdo/ipc/目录下的头文件是不能直接使用的。这些module可以在.cfg配置文件中进行配置,在C/C++源文件中只需要看上面的头文件里提供的API就可以了。

核间通信的初始化

  核间通信的初始化会用到三个module,分别是Ipc,MultiProc和SharedRegion。Ipc模块可以用ProcSync_ALL属性来设置成自动同步每个核,这样在执行完Ipc_start()之后,每个核之间就同步完成了,不需要手动去调用Ipc_attach()。有时候我们不需要每个核之间都同步时就可以去手动调用Ipc_attach(),让特定的几个核之间建立联系。
  Ipc_attach()要成功,还需要用到MultiProc模块,它可以设置我们的多核程序需要用到那几个核,当前这个工程是针对哪个核来编写的,例如下面我准备用4个核,然后当前的配置是针对核0来编写的。核0执行Ipc_start()的时候就会尝试去和另外三个核建立联系。
  核间同步需要用到一块共享的数据区,因此需要用到SharedRegion,SharedRegion0会被用来做这些事,大概会需要用几百个字节吧,剩下的空间用户仍然可以继续使用。

var Ipc = xdc.useModule('ti.sdo.ipc.Ipc');
var MultiProc = xdc.useModule('ti.sdo.utils.MultiProc');
var SharedRegion = xdc.useModule('ti.sdo.ipc.SharedRegion');Ipc.procSync = Ipc.ProcSync_ALL;MultiProc.baseIdOfCluster = 0;
MultiProc.numProcessors = 4;
MultiProc.setConfig("CORE0", ["CORE0", "CORE1", "CORE2", "CORE3"]);var SMSMC_BASE = 0x0C000000;
var SMSMC_SIZE = 0x00080000;SharedRegion.numEntries = 4;
SharedRegion.translate = false;
SharedRegion.setEntryMeta(0,{ base: SMSMC_BASE, len:  SMSMC_SIZE,ownerProcId: 0,isValid: true,cacheEnable: true,cacheLineSize: 64,createHeap: true,name: "MSMC_SHARED",});

  上面是一个配置的示例,在这之前,每个核的platform一定要有一块相同物理地址的共享存储区。如果它们在不同核中的逻辑地址不同,那么需要SharedRegion.translate设置成true,进行地址转换。我在MSMC中选择512kB用作SharedRegion0,它在每个核中的地址都是相同的所以可以设置成false,节省资源。SharedRegion的cacheLineSize需要根据实际情况设置,MSMC会被L1D cache,查阅文档可以指导L1D cache的cacheline是64字节。

// IPC initialization
status = Ipc_start();
if(status < 0){System_abort("Ipc start failed\n");
}

  配置完成后,在每个核的main函数中最开始的位置执行Ipc_start(),不出意外每个核之间会完成同步,之后就可以执行其它的IPC模块的API。可以在调试程序,然后通过ROV查看Ipc模块的状态,下图是核0与其它三个核的同步的情况,attached都是true表明核0已经与其它三个和都同步上了。
在这里插入图片描述

Notify

  Notify适合用来发送简短的信息,因为Notify可以带一个32bit的payload。Notify通信的基础是IPC寄存器,C6678的IPC寄存器中只有4-31共28个事件源。某个核如果想要收到另一个核的Notify,就需要先注册这个Notify,将某个事件源和一个回调函数(callback)绑定。
在这里插入图片描述

  我没有为核0注册Notify事件,但是它本身就已经有了两个事件Id被注册了。所以用的时候要注意,不要用2号和4号EventId,会产生冲突。用5-31号的EventId应该没问题。提到这个我还想到,DSP的CorePac里4-15的中断Id里,5号中断已经被用来处理IPC中断了,14号中断用来处理定时器中断,因此这两个中断号也是不能用的。

MessageQ

  Notify能够发送的数据有限,但如果只是发送一个数据块,Notify也可以只发送一个数据块的首地址指针。MessageQ可以用来发送批量的数据,而且可以很方便地与任务调度结合起来。比如一个Task如果需要等待另一个核产生的数据,如果用Notify实现,那么需要在Notify的Callback函数里产生一个Semaphore,在Task里等待这个Semaphore。而MessageQ则可以只通过MessageQ_get()函数实现接收数据和Semaphore等待的功能。MessageQ相比于Notify更大的优势在于它是一个队列,可以对收到的消息按照优先级排队,处理器可以依次处理每个消息。
在这里插入图片描述
  通过MessageQ发送的Message的长度可以是任意的,我们可以根据自己的需求定义数据结构。比如我最近用Core1调用Core3执行FFT,FFT计算完成后Core3通过Notify告知Core1计算完成。那么Core1就需要利用MessageQ将一些必要的参数告诉Core3。因此我定义了以下的数据结构FftMsg,其中包括FFT计算的类型、源数据地址、目的数据地址、二维FFT的长和宽。只要保证这个数据结构最前面是MessageQ_MsgHeader,后面可以随意。header里面本身是由一个replyQueueId的,可以方便接收方将Message返回给发送方。header里面还有一个MessageId,用来区分发往同一个MessageQ的不同类型的Message。我自己又加了一个nReplyCoreId,用来告诉Core3在FFT计算完成后,要向哪个Core发送Notify。

typedef enum FFT2dType
{FFT2d_R2R = 0,FFT2d_R2C,FFT2d_R2C_N,IFFT2d_C2R
} FFT2dType;typedef struct FftMsg{MessageQ_MsgHeader header;FFT2dType eType;float *pSrc;float *pDst;int nWidth;int nHeight;Uint16 nReplyCoreId;
} FftMsg;typedef FftMsg *FftMsgHandle;

  MessageQ是由接收方创建的,Core1要往Core3发送Message,Core1只需要打开Core3创建的MessageQ即可。有可能会打开失败,因为Core3可能一开始还没创建好这个MessageQ,所以做了一个do-while循环。
  Core1在调用之前一定要确保源数据都已经写回共享的存储区,如果还留在Core1本地的Cache里,那么计算结果就会出错。
  要发送的Message是动态分配的,一般是动态分配在一个SharedRegion里构建的堆区,每个核都需要为同一个堆区注册一个相同的HeapId,MessageQ_alloc靠HeapId来决定在哪里动态分配数据空间。
  Mesage可以设置优先级,优先级只有三种,默认是Normal,较高的是MessageQ_HIGHPRI,最高优先级是MessageQ_URGENTPRI。

// Core1 code, FFT caller
FftMsgHandle hFftCmd;
MessageQ_QueueId fftQueueId;do{status = MessageQ_open(MSGQ_FFTCMD_NAME, &fftQueueId);if(status < 0){Task_sleep(1);}
} while (status < 0);Cache_wbInv(pData, KCF_DFT_SIZE * KCF_DFT_SIZE * sizeof(float), Cache_Type_ALLD, TRUE);
// FFT
hFftCmd = (FftMsgHandle)MessageQ_alloc(nHeapId, sizeof(FftMsg));
if(hFftCmd == NULL){System_abort("Message alloc failed\n");
}
hFftCmd->eType = FFT2d_R2R;
hFftCmd->nWidth = 32;
hFftCmd->nHeight = 32;
hFftCmd->pSrc = pData;
hFftCmd->pDst = pData;
hFftCmd->nReplyCoreId = 1;
// MessageQ_setMsgId(hFftCmd, MSG_C1FFT_ID);
if(m_nCoreId == 1){MessageQ_setMsgPri(hFftCmd, MessageQ_HIGHPRI);
}status = MessageQ_put(fftQueueId, (MessageQ_Msg)hFftCmd);
if(status < 0){System_abort("MessageQ put failed\n");
}Semaphore_pend(hFFTDoneSem, BIOS_WAIT_FOREVER);

  Core3是接收方,首先需要创建一个MessageQ来接收Message。然后就可以不停等待Message,就像作为一个服务器不断处理客户端发过来的计算任务。

// Core3 code, FFT callee
MessageQ_Handle hFftCmdQueue;
FftMsgHandle hFftMsg;// using default synchronizer SyncSem
hFftCmdQueue = MessageQ_create(MSGQ_FFTCMD_NAME, NULL);
if(hFftCmdQueue == NULL){System_abort("MessageQ create failed\n");
}for (;;){MessageQ_get(hFftCmdQueue, (MessageQ_Msg *)&hFftMsg, MessageQ_FOREVER);// TODO FFT// free messageMessageQ_free((MessageQ_Msg)hFftMsg);// Send Notifydo{status = Notify_sendEvent(hFftMsg->nReplyCoreId, NOTIFY_LINEID, FFT_DONE_EVTID, 0, TRUE);} while (status < 0);
}

GateMP

  GateMP用来让多核互斥访问某些资源,这个我暂时还没用到,但用起来也挺简单的,文档上也都写得很清楚。


http://chatgpt.dhexx.cn/article/MRTFvWTw.shtml

相关文章

近期C6000 DSP开发小结

使用C开发DSP 如果你也跟我一样刚开始接触C6000系列的DSP&#xff0c;我觉得可以尝试一下用C来开发&#xff0c;虽然说这么做代码的执行效率可能会比C或者纯汇编的开发来得低&#xff0c;但它胜在能够让整个工程的脉络更加清晰。 面向C6000开发的cl6x编译器对C有比较好的支持。…

【DSP开发】CCS5.5测试代码运行时间

1、进入CCS环境&#xff0c;load已有工程.out文件&#xff0c;找到要查看的代码执行周期的地方。 2、选择CCS菜单中的Run——Clock——Enable 3、选择Run——Clock——Setup 4、在左下角观察时钟周期 5、 至此就可以解决在代码中引入<time.h>后输出为0的情况&#xff…

ADI DSP开发环境(CCES)下的程序烧录问题(以ADSP-SC589为例)

在CCES环境下将程序烧录到flash主要有两种方式。 1.通过命令窗口 首先在对项目编译产生文件的属性设置为Release。 在编译完成后&#xff0c;找到每个CORE单独生成的DXE文件&#xff1a; 文件通常位于项目目录下每个core的Release文件中。 安装SC589评估板的驱动&#xff…

DSP开发环境及工具之CCS

DSP开发环境及工具之CCS CCS( Code Composer Studio)是美国德州仪器(TI)公司的嵌入式处理器的开发环境,可以用于TI公司的各个系列处理器的软件开发和调试,如DSP,MCU,ARM等。 主要的操作都是在这个窗口之间做相应的切换。 创建工程文件 或者

【FPGA-DSP】第二期:DSP开发流程【全过程】

目录 1. System Generator安装 1.1 system generator的安装 1.1.1 vivado安装System Generator 1.1.2 System Generator配置 1.3 启动 2. FPGA-DSP开发流程 2.1 FPGA-DSP 开发流程介绍 2.2 FPGA-DSP 实际开发流程 1. 软件启动 2. matlab编写 3. Simulink仿真 Simu…

DSP开发笔记一

前言 ​ 本笔记首先对DSP的特点及其选型进行了描述&#xff0c;然后重点记录DSP开发环境的搭建及基础工程示例&#xff0c;对为DSP开发新手有一定的指导作用。 1. DSP简介 1.1 主要特点 在一个指令周期内可完成一次乘法和一次加法&#xff1b;程序和数据空间分开&#xff0…

UML状态图示例

状态图是用于表示对象状态的UML图。 在图书管理系统中&#xff0c;图书有不同的状态&#xff0c;我们可以用状态图表示如下&#xff1a;

数据库课程设计——宾馆管理系统UML状态图

客户账号状态图 前台登录状态图 客户预订状态图 会员卡状态图

【转】超详细的UML状态图符号,初学者也能轻松看懂状态图

UML状态图&#xff0c;用于显示状态机&#xff0c;即描述一个对象所处的可能状态以及状态之间的转移。用状态图建模可以帮助开发人员分析复杂对象的各种状态的转换&#xff0c;以及对象何时执行怎样的动作。那状态图又是怎样表示这些信息的呢&#xff1f;要想看明白其中的奥妙&…

菜鸟实战UML——状态图

状态图 状态图(Statechart Diagram)&#xff1a;是描述一个实体基于事件反应的动态行为&#xff0c;显示了该实体如何根据当前所处的状态对不同的事件做出反应。通常我们创建一个UML状态图是为了以下的研究目的&#xff1a;研究类、角色、子系统、或组件的复杂行为。 理解&am…

UML——活动图和状态图

目录 活动图 活动图的基本要素 状态图 状态图的基本要素 状态图与活动图之间的区别 活动图 概念&#xff1a;活动图本质上是一种流程图&#xff0c;它描述活动的序列&#xff0c;即系统从一个活动到另一个活动的控制流。 作用 描述一个操作的执行过程中所完成的工作或者…

UML状态图 2021.07.18

概述 UML状态图主要用于描述对象具有的各种状态、状态之间的转换过程以及触发状态转换的各种事件和条件。 UML 状态图的目的: UML 状态图可以捕获对象、子系统和系统的生命周期&#xff0c;可以告知一个对象可以拥有的状态&#xff0c;并且事件(如消息的接收&#xff0c;时间…

UML状态机图

状态机图&#xff08;State Machine Diagram&#xff09;也叫状态图、有限状态机图&#xff08;Finite Diagram&#xff09;&#xff0c;是一种描述所有状态及状态之间流转规则的图形。在软件设计领域&#xff0c;“状态”在业务系统中无处不在&#xff1a;订单要有状态&#x…

UML—状态图

【内容】 1.什么是状态图 状态图描述一个特定对象的所有可能状态以及由于各种事件的发生而引起的状态之间的转移。状态图侧重于从行为的结果来描述&#xff0c;只涉及一个特定的对象&#xff0c;常用于动态特性建模。 2.状态图的组成 &#xff08;1&#xff09;起点、终点 …

UML——状态图

7 状态图&#xff08;Stage Diagram&#xff09; 7.1 概述 状态图主要用于描述对象的状态变化以确定何种行为改变了对象状态&#xff0c;以及对象状态变化对系统的影响。 通常只用于描述单个对象的行为。 状态图在描述单个复杂对象的行为时非常有助于我们理解一个对象的行为…

UML活动图与状态图

笔记基于《UML和模式应用》教材 UML活动图 基本的UML表示法 基本的UML活动图表示法&#xff0c;包括动作、分区、分叉点、连接点和对象节点等。分区有助于观察多个参与者以及业务过程中涉及的并行动作&#xff0c;对象节点可以描述动作周围移动的事物。 其他UML活动图表示法…

UML状态图和活动图

转载于https://www.cnblogs.com/jingwhale/p/4230235.html UML状态图和活动图 UML状态图和活动图 统一建模语言UML&#xff08;Unified Modeling Language&#xff09;是非专利的第三代建模和规约语言。UML是一种开放的方法&#xff0c;用于说明、可视化、构建和编写一个正在…

UML图之『状态图』就是这么简单搞定

UML图之状态图 状态图的组成元素&#xff1a; 初始状态 初始状态是状态机的起始位置&#xff0c;它不须要事件的触发。由黑色实心圆点来表示 状态 状态是对象运行某项活动或等待某个事件的条件。 状态用圆角矩形表示 复合状态 复合状态是具有子状态&#xff08;或称为嵌…

UML 状态图 statechart diagram

1. 什么是状态图 状态图是一个类对象所可能经历的所有历程的模型图。状态图由对象的各个状态和连接这些状态的转换组成。说明对象在它的生命期中响应事件所经历的状态序列&#xff0c;以及它们对那些事件的响应。 2. 状态图的作用 1). 状态图描述了状态之间的转换顺序&#x…

UML状态图

状态图&#xff08;Statechart digram&#xff09;是系统分析的一种常用工具&#xff0c;系统分析员在对系统建模时&#xff0c;最先考虑的不是基于活动之间的控制流&#xff0c;而是基于状态之间的控制流&#xff0c;因为系统中对象的状态变化最易被发现和理解。 状态机…