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

news2024/12/26 10:56:12

  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 message
    MessageQ_free((MessageQ_Msg)hFftMsg);

    // Send Notify
    do{
        status = Notify_sendEvent(hFftMsg->nReplyCoreId, NOTIFY_LINEID, FFT_DONE_EVTID, 0, TRUE);
    } while (status < 0);
}

GateMP

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/173402.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Docker 应用实践-容器篇

在 Docker 镜像篇中&#xff0c;我们了解到 Docker 镜像类似于模板&#xff0c;那么 Docker 容器就相当于从模板复制过来运行时的实例&#xff0c;Docker 容器可以被创建、复制、暂停和删除等。 每一个 Docker 容器在运行时都是以镜像为基础层&#xff0c;并在镜像的基础上创建…

PWM 应用

1.PWM 同样也是通过 sysfs 方式进行操控&#xff0c;进入到/sys/class/pwm 目录下。8 个以 pwmchipX&#xff08;X 表示数字 0~7&#xff09;命名的文件夹&#xff0c;这八个文件夹其实就对应了 I.MX6U的 8 个 PWM 控制器&#xff0c; I.MX6U 总共有 8 个 PWM 控制器。2.进入到…

云GPU服务器部署及pycharm远程连接

我们在之前使用CoLab来运行项目&#xff0c;但其存在时长限制问题所以并不是很理想&#xff0c;今天博主发现腾讯云服务目前在搞活动&#xff0c;比较实惠&#xff0c;便买了一台来体验一下。 直接搜索gpu服务器租用即可找到 购买服务器 博主买的是NVIDIA T4 GPU,还是较有性…

web流程设计器andflow_js已支持自定义颜色

andflow_js 是一个web 开源流程设计框架&#xff0c;目前版本已支持对各类节点单独设置颜色。除了颜色之外&#xff0c;andflow_js还支持通过setActionInfo、setGroupInfo、setListInfo、setTipInfo 等设置节点各种参数。 设置节点的颜色&#xff1a; 设置节点边框颜色 and…

Arduino环境下对NodeMCU ESP8266的闪存flash系统使用

flash存储简答介绍 参考&#xff1a;https://www.elecfans.com/consume/572040.html flash存储器又称闪存&#xff08;快闪存储器&#xff09;&#xff0c;就其本质而言&#xff0c;flash存储器属于EEPROM&#xff08;电擦除可编程只读存储器&#xff09;类型。是一种长寿命的…

矩阵求导学习

布局 分子布局 ∂y∂x(∂y∂x1∂y∂x2⋯∂y∂xn)\frac{\partial y}{\partial \mathbf{x}} \begin{pmatrix} \frac{\partial y}{\partial x_1} & \frac{\partial y}{\partial x_2} &\cdots & \frac{\partial y}{\partial x_n} \end{pmatrix} ∂x∂y​(∂x1​∂y​​…

tkinter绘制组件(38)——状态开关按钮

tkinter绘制组件&#xff08;38&#xff09;——状态开关按钮引言布局函数结构按钮主体渐变色处理颜色处理基础渐变色列表形成列表样式绑定完整函数代码效果测试代码最终效果github项目pip下载结语引言 TinUI里的状态开关按钮&#xff08;togglebutton&#xff09;和开关&…

DPU网络开发SDK—DPDK(五)

rte_eal_init 接上次内容继续对rte_eal_init()所做的工作进行分析。 18. 检查是否允许直接物理地址访问 rte_eal_using_phys_addrs()会去检查当前系统是否允许在进程虚拟地址空间直接访问物理地址。需要有两个支持条件&#xff1a;存在大页内存和能够进行虚拟地址到物理地址…

智云通CRM:如何把握拓客成交的三种时间?

在我们邀约、拜访及展示产品之后&#xff0c;客户就一定会成为你的客户吗&#xff1f;当然不尽然&#xff0c;这取决于众多因素。任何事情都不是一蹴而就的&#xff0c;我们不能刚到某个场合认识了一个人&#xff0c;就一定要立即成交&#xff0c;或者反过来因为对方此时没有需…

Pytorch实战笔记(2)——CNN实现情感分析

本文展示的是使用 Pytorch 构建一个 TextCNN 来实现情感分析。本文的架构是第一章详细介绍 TextCNN&#xff08;不带公式版&#xff09;&#xff0c;第二章是核心代码部分。 目录1. TextCNN2. TextCNN 实现情感分析参考1. TextCNN 相较于 LSTM 而言&#xff0c;我个人其实是没…

手把手教你学51单片机-C语言基础

二进制、十进制和十六进制 对于二进制来说,8 位二进制我们称之为一个字节。 我们在进行 C 语言编程的时候,我们只写十进制和十六进制,那么不带 0x 的就 是十进制,带了 0x 符号的就是十六进制。 C 语言变量类型和范围 C 语言的数据基本类型分为字符型、整型、长整型以及…

【算法题解】12. 删除链表中的节点

文章目录题目题解Java 代码实现Go 代码实现复杂度分析这是一道简单题&#xff0c;题目来自 leetcode 题目 给定一个单链表的 head&#xff0c;我们想删除它其中的一个节点 node。 只给你一个需要删除的节点 node 。你将 无法访问 第一个节点 head。 链表的所有值都是 唯一的&…

1.吴恩达机器学习课程笔记:多元梯度下降法

1.吴恩达机器学习课程笔记&#xff1a;多元梯度下降法 笔记来源&#xff1a;吴恩达机器学习课程笔记&#xff1a;多元梯度下降法 仅作为个人学习笔记&#xff0c;若各位大佬发现错误请指正 1.1 多元特征&#xff08;变量&#xff09; 每一列代表一个特征&#xff0c;例如&…

FMC子卡设计资料原理图:FMC550-基于ADRV9002双窄带宽带射频收发器FMC子卡

FMC550-基于ADRV9002双窄带宽带射频收发器FMC子卡一、产品概述 ADRV9002 是一款高性能、高线性度、高动态范围收发器&#xff0c;旨在针对性能与功耗系统进行优化。该设备是可配置的&#xff0c;非常适合要求苛刻、低功耗、便携式和电池供电的设备。ADRV9002 的工作频率为 …

计算机基础——无处不网络(2)

作者简介:一名云计算网络运维人员、每天分享网络与运维的技术与干货。 座右铭:低头赶路,敬事如仪 个人主页:网络豆的主页​​​​​​ 目录 前言 一.计算机网络的接入方式 1

青龙脚本-稳定阅读

稳定阅读积分 查看请求头的cookie 变量 export zzbhd 多号或换行 */ 脚本附后 /* 查看请求头的cookie 变量 export zzbhd 多号或换行*/const $ new Env(至尊宝阅读); const axios require(axios); let request require("request"); request request.defaults(…

纯手工模拟Vue中的数据劫持和代理

为什么要实现数据劫持和代理 举一个场景&#xff1a;比如在小程序开发中&#xff0c;我们需要逻辑层修改的数据能同步响应更新到视图层的页面上&#xff0c;那么底层框架在实现这种效果的时候&#xff0c;机制是什么样的呢&#xff1f; 其实这里的底层原理类似于Vue中的数据劫…

基于蜣螂算法优化的BP神经网络(预测应用) - 附代码

基于蜣螂算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码 文章目录基于蜣螂算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码1.数据介绍3.蜣螂优化BP神经网络3.1 BP神经网络参数设置3.2 蜣螂算法应用4.测试结果&#xff1a;5.Matlab代码摘要&am…

ClickHouse快速复习

ClickHouse​一.特性​1.列式数据库管理系统​2.数据压缩​3.数据的磁盘存储​4.支持SQL​5.索引​6.适合在线查询​7.支持数据复制和数据完整性​8.实时的数据更新​9.处理大量短查询的吞吐量​10.处理大量短查询的吞吐量​11.限制​二.数据类型​1.数字类型​2.浮点数(float)…

前端都在聊什么 - 第 1 期

Hello 小伙伴们早上、中午、下午、晚上、深夜好&#xff0c;我是爱折腾的 jsliang~「前端都在聊什么」是 jsliang 日常写文章/做视频/搞直播过程中&#xff0c;小伙伴们的提问以及我的解疑整理。本期对应 2023 年的 01.01-01.15 这个时间段。本期针对「工作」「学习」「规划」「…