Openvx Tiovx技术杂谈

Openvx & Tiovx技术杂谈

Openvx & Tiovx (六) Host & Target

https://zhuanlan.zhihu.com/p/474701695

https://dev.ti.com/tirex/explore/node?node=ANd.gAKGXC97FboluTIMhw

Openvx 保留了代码移植到多核平台的可能性。可能是因为多核平台的种类繁多,Openvx 在定义时并没有引入太多与多核平台相关的概念,也没有多少与多核平台相关的 API。在我印象中与多核平台相关的 API 只有 vxSetNodeTarget() 和 vxSetImmediateModeTarget()。以 vxSetNodeTarget() 为例,该 API 用于设定节点在哪一个核上运行。

由于 Ti 公司的主流产品为多核平台,Tiovx 扩展 Openvx 的重点之一是实现图对多核资源的高效利用,于是 Host 和 Target 是 Tiovx 中非常重要的概念。

Host & Target 分别指什么

说实话,我觉得 Tiovx 的官方文档并没有很好地解释什么是 Host、什么是Target。根据 Tiovx 的源码以及我的实践经验,Host 和 Target 的概念可以如下概括:

  • Host 和 Target 是针对具体的一个图而言的。
  • 负责创建图的核为 Host,负责运行节点的核为 Target。
  • 如果 Host 也会负责运行部分节点,那么该核同时也为 Target。

例如有核 A 和核 B,在核 A 上创建一个包含两个节点 N1 和 N2 的图 G,则针对该图 G 而言,核 A 为 Host。如果 N1 和 N2 均在 核 B 上运行,则针对该图 G 而言核 B 为 Target。如果 N1 在核 B 上运行,N2 在核 A 上运行,则针对该图 G 而言核 A 是 Host 的同时也是 Target。

核间通信及资源共享

在谈及多核平台时,须有如下概念:

  • 每个核都会独立运行一个操作系统,如 Linux、QNX、RTOS。每个操作系统都可能运行多个进程/线程/任务。
  • 保持系统之间的协作必须实现一个合适的通信协议。

Tiovx 并没有指定特定的通信协议,在代码中以 IPC (Inter-Processor Communication,处理器间通信) 代指。这很好理解,因为实现通信协议是其它模块的事,Tiovx 只负责调用相关接口。从源码中可以看出,Tiovx 模块发送的消息的载荷很短,仅包含 Target id 以及命令码两部分。命令主要包含:

  • 图节点的创建。
  • 图节点的析构。
  • 图节点的控制。
  • 图节点的执行。
  • 图节点执行完毕后的回调处理。

此外,Tiovx 还专门创建一个线程或任务来处理通信消息。

以上内容可在源码 (下载地址见参考资料) 中的 vx_target.c 文件查找到。

Tiovx 采用共享内存的方式实现资源共享,该共享内存是外部物理内存的一块指定区域。Tiovx 会将该段共享内存的首地址保存在变量 gTivxObjDescShmEntry 中,该变量用于存储对象的描述符 —— 每个对象 (节点对象、图像对象等) 都有对应的一个描述符 (本质是一个结构体),描述符包含对象类型、对象数据存储空间的首地址、对象数据存储空间的大小等重要信息。即 Tiovx 共享的资源是对象的描述符,通过对象的描述符就可以访问对象的所有信息。

注意,不同操作系统使用不同的虚拟地址空间,即同样的一个地址,在不同虚拟地址空间中对应不同的物理地址。Tiovx 引入 shared_ptr 作为中间载体,这里假设 shared_ptr 为物理地址 (根据实际应用,也可以采用其它形式的地址)。在 Host 端,Tiovx 为对象分配数据存储空间之后,会自动调用 tivxMemHost2SharedPtr() 函数将存储空间的首地址 (Host 的虚拟地址) 转为物理地址。在 Target 端,如要访问对象数据存储空间,要主动调用 tivxMemShared2TargetPtr() 函数将物理地址转为 Target 端的虚拟地址。

 

核间通信及资源共享示意

 

参考文献链接

https://zhuanlan.zhihu.com/p/474701695

https://link.zhihu.com/?target=https%3A//software-dl.ti.com/jacinto7/esd/processor-sdk-rtos-jacinto7/latest/exports/docs/tiovx/docs/user_guide/index.html

https://dev.ti.com/tirex/explore/node?node=ANd.gAKGXC97FboluTIMhw

Openvx & Tiovx (七) 自定义 Kernel (Tiovx 拓展)

https://zhuanlan.zhihu.com/p/476213104

https://software-dl.ti.com/jacinto7/esd/processor-sdk-rtos-jacinto7/09_02_00_05/exports/docs/psdk_rtos/docs/user_guide/index.html

https://link.zhihu.com/?target=https%3A//software-dl.ti.com/jacinto7/esd/processor-sdk-rtos-jacinto7/latest/exports/docs/tiovx/docs/user_guide/index.html

Target Kernel & Target Kernel Instance

Tiovx 在自定义 Kernel 上的拓展与如何在 Target 上执行节点是同源的,Tiovx 给出的解决方案如下:

  • 引入 Target Kernel 对象以及 Target Kernel Instance 对象。
  • Target Kernel 对象与节点的执行函数绑定。
  • 原则上相关代码仅存在于 Target 上,除非 Host 同时也作为 Target。
  • Target Kernel 对象以及 Target Kernel Instance 对象与 Context 解绑,目的是节省资源。
  • Target Kernel 对象与 Target Kernel Instance 对象的关系正如 Kernel 对象与节点对象的关系,后者关系见我在 Openvx & Tiovx (三) 自定义 Kernel 文章中的描述。
  • 采用独立的列表维护注册的 Target Kernel 对象,该列表在源码中为 g_target_kernel_table
  • 采用独立的列表维护创建的 Target Kernel Instance 对象,该列表在源码中为 g_target_kernel_instance_table

Kernel 对象与 Target Kernel 对象的关系

Kernel 对象存在于 Host 上,回忆我在 Openvx & Tiovx (三) 自定义 Kernel 文章中的描述,在 Context 上注册的 Kernel 对象必须有唯一的名称及枚举值。

Target Kernel 对象存在于 Target 上,与 Kernel 类似,在 g_target_kernel_table 上注册的 Target Kernel 对象都必须有唯一的名称及枚举值。

不强制 Host 上的 Kernel 对象与 Target 上的 Target Kernel 对象一一对应。如果 Target 上某个 Target Kernel 对象正好与 Host 上某个 Kernel 对象有相同的名称及枚举值,则该 Kernel 对象生成的节点对象可以在 Target 上运行。

节点对象与 Target Kernel Instance 对象的关系

在 Tiovx 创建图之后 (调用 vxVerifyGraph() 函数之后), 节点对象的对象描述符会保存对应的 Target Kernel Instance 对象信息。Target 通过节点对象描述符获得 Target Kernel Instance 对象,再通过 Target Kernel Instance 对象间接调用对应的 Target Kernel 对象的执行函数,从而实现节点的执行。

(节点描述符是什么、Target 为何能获取 Host 上创建的节点描述符见 Openvx & Tiovx (六) Host & Target。)

 

Kernel 对象、节点对象、Target Kernel 对象、Target Kernel Instance 对象之间的关系

注册自定义 Kernel

Host 端

在 Host 端的注册方式与 Openvx 原生的注册方式几乎一致 (详见 Openvx & Tiovx (三) 自定义 Kernel) 除了:

  • 调用 vxAddUserKernel() 函数时参数 func_ptr 为 NULL。Tiovx 中负责执行节点的是 Target,故在 Host 上注册时不需要提供执行函数。(注:实际上也可以提供执行函数,此时节点会在 Host 上执行。)
  • 调用 vxFinalizeKernel() 函数完成注册前,需要调用 tivxAddKernelTarget() 函数设定能执行节点的 Target。可以多次调用该函数设定多个 Target。节点默认在第一个 Target 上执行,可以调用 vxSetNodeTarget() 函数修改执行节点的 Target。tivxAddKernelTarget() 函数的声明如下:

vx_status tivxAddKernelTarget(

    vx_kernel kernel,        /* 调用 vxAddUserKernel() 函数返回的 Kernel 对象 */

    const char *target_name  /* 支持执行对应节点的 Target 名称 */

);

此外,Tiovx 提供 tivxCreateNodeByKernelEnum() 函数及 tivxCreateNodeByKernelName() 函数,以快速通过 Kernel 对象的枚举值或名称生成对应的节点对象并与数据对象进行绑定。例如 Tiovx 封装 vxAndNode() 函数的代码只有如下几行:

vx_node vxAndNode(vx_graph graph, vx_image in1, vx_image in2, vx_image out)

{

    /* 参数的顺序要与期望的一致 */

    vx_reference params[] = {

       (vx_reference)in1,

       (vx_reference)in2,

       (vx_reference)out,

    };

    /* VX_KERNEL_AND 是 Kernel 对象的枚举值 */

    /* dimof 是 Tiovx 提供的宏函数,用于获取数组的元素个数 */

    return tivxCreateNodeByKernelEnum(graph, (vx_enum)VX_KERNEL_AND, params, dimof(params));

}

Target 端

在 Target 端注册的流程为

  • 实现 Target Kernel Instance 对象的初始化函数。初始化函数主要负责申请必要的动态内存。
  • 实现 Target Kernel Instance 对象的析构函数。析构函数主要负责释放初始化函数申请的动态内存。
  • 实现 Target Kernel Instance 对象的执行函数。
  • 调用 tivxAddTargetKernel() 函数或 tivxAddTargetKernelByName() 函数完成注册。(二选一,建议选前者。)

Target Kernel Instance 对象的初始化函数、析构函数、执行函数的原型均为:

/*

 * @param[in] kernel Target Kernel Instance 对象

 * @param[in] obj_desc 参数的对象描述符列表,与节点对象绑定的数据对象一一对应

 * @param[in] num_params 参数个数

 * @param[in] priv_arg 可选参数

 */

typedef vx_status(*tivx_target_kernel_f)(tivx_target_kernel_instance kernel, tivx_obj_desc_t *obj_desc[], uint16_t num_params, void *priv_arg);

注:参数 obj_desc 是对象描述符列表,执行函数可以通过列表中的对象描述符直接访问数据,当然需要通过 tivxMemShared2TargetPtr() 函数进行地址空间的转换。

 

tivxAddTargetKernel() 函数及 tivxAddTargetKernelByName() 函数的声明如下

/*

 * @param[in] kernel_id 枚举值,需要与对应的 Kernel 对象的枚举值相同

 * @param[in] target_name 必须为注册所在的 Target 的名称

 * @param[in] process_func 执行函数

 * @param[in] create_func 初始化函数

 * @param[in] delete_func 析构函数

 * @param[in] control_func 控制函数,通常为 NULL,即不需要提供

 * @param[in] priv_arg 额外参数,通常为 NULL,即不需要提供

 */

tivx_target_kernel tivxAddTargetKernel(

    vx_enum kernel_id,

    const char *target_name,

    tivx_target_kernel_f process_func,

    tivx_target_kernel_f create_func,

    tivx_target_kernel_f delete_func,

    tivx_target_kernel_control_f control_func,

    void *priv_arg

);

 

 

/*

 * @param[in] kernel_name 名称,需要与对应的 Kernel 对象的名称相同

 * @param[in] target_name 必须为注册所在的 Target 的名称

 * @param[in] process_func 执行函数

 * @param[in] create_func 初始化函数

 * @param[in] delete_func 析构函数

 * @param[in] control_func 控制函数,通常为 NULL,即不需要提供

 * @param[in] priv_arg 额外参数,通常为 NULL,即不需要提供

 */

tivx_target_kernel tivxAddTargetKernelByName(

    const char *kernel_name,

    const char *target_name,

    tivx_target_kernel_f process_func,

    tivx_target_kernel_f create_func,

    tivx_target_kernel_f delete_func,

    tivx_target_kernel_control_f control_func,

    void *priv_arg

);

调用 tivxRemoveTargetKernel() 函数注销已注册的 Target Kernel 对象。

不用考虑 Target Kernel Instance 对象如何创建或执行,Tiovx 框架会自动完成这些工作。

 

 

参考文献链接

https://zhuanlan.zhihu.com/p/476213104

https://software-dl.ti.com/jacinto7/esd/processor-sdk-rtos-jacinto7/09_02_00_05/exports/docs/psdk_rtos/docs/user_guide/index.html

https://link.zhihu.com/?target=https%3A//software-dl.ti.com/jacinto7/esd/processor-sdk-rtos-jacinto7/latest/exports/docs/tiovx/docs/user_guide/index.html

Openvx & Tiovx (八) 如何基于 Tiovx 编写应用程序

https://zhuanlan.zhihu.com/p/476569209

https://software-dl.ti.com/jacinto7/esd/processor-sdk-rtos-jacinto7/latest/exports/docs/tiovx/docs/user_guide/index.html

https://www.ti.com/tool/download/PROCESSOR-SDK-RTOS-J721E

Ti 提供的 PROCESSOR-SDK-RTOS-J721E_08.01.00.13) 中包含了非常丰富的例子,详见 vision_apps 文件夹。毫无疑问,这些例子能帮助我们更好地理解如何在 Ti 平台上部署程序。但如果你只想知道如何基于 Tiovx 编写应用程序,这些例子毫无疑问会带来额外的学习成本:

  • 包含大量其它模块的初始化代码,如日志模块的初始化。
  • 如果程序运行在 RTOS 系统上,则会包含大量 RTOS 系统的初始化代码,如中断的初始化。

本文结合我个人的实践经验,从 Ti 的例子中抽丝剥茧,得出基于 Tiovx 编写应用程序的代码示例。这里要强调一下,如果需要在 Ti 平台上部署应用,还是要多多参考 Ti 提供的例子。

Host 端

/* vx.h 及 tivx.h 这两个头文件是必须要包含的,其它头文件按需包含 */

#include <VX/vx.h>

#include <TI/tivx.h>

 

/* 从本地文件读取图片并保存到图像对象中。 */

/* 这里不提供实现。可以通过 Opencv 实现。 */

extern vx_status read_img_from_file(const char *file_name, vx_image img);

 

/* 将图像对象中的数据保存到本地文件中。 */

/* 这里不提供实现。可以通过 Opencv 实现。 */

extern vx_status write_img_to_file(const char *file_name, vx_image img);

 

/* 本示例使用的图,作用是将 RGB 图片转为灰度图 */

static vx_status add_rgb2gray_to_graph(vx_graph graph, vx_image src, vx_image dst);

 

int main(int argc, char *argv[])

{

    /* 初始化 Tiovx 框架,包括共享内存初始化、内部日志初始化、创建 IPC 处理线程(或任务)等 */

    /* 根据 Tiovx 库的编译条件,可能还会注册 Openvx 原生 Kernel 在 Target 端的部分。 */

    /* 注:由 Ti 的 RTOS SDK 源码得知,Tiovx 框架下只希望由 DSP 执行节点,不希望 ARM 执行节点。 */

    tivxInit();

    /* 加载 Openvx 原生 Kernel 模块,加载后才能在接下来的代码中注册 Openvx 原生 Kernel 在 Host 端的部分。*/

    tivxHostInit();

   

    vx_status status = VX_SUCCESS;

    vx_uint32 width = 1024, height = 720;

   

    /* 创建 Context 对象并初始化、注册 Openvx 原生 Kernel 在 Host 端的部分。 */

    vx_context context = vxCreateContext();

 

 

    /* 如有必要,在这里注册自定义 Kernel 在 Host 端的部分。 */

 

 

    vx_graph graph = vxCreateGraph(context);

    vx_image src = vxCreateImage(context, width, height, VX_DF_IMAGE_RGB);

    vx_image dst = vxCreateImage(context, width, height, VX_DF_IMAGE_U8);

   

  

    /* ====== 图像处理逻辑开始 ====== */

    status = add_rgb2gray_to_graph(graph, src, dst);

    if (VX_SUCCESS == status) {

         status = read_img_from_file("input.png", src);

    }

    if (VX_SUCCESS == status) {

         status = vxVerifyGraph(graph);

    }

    if (VX_SUCCESS == status) {

         status = vxScheduleGraph(graph);

    }

    if (VX_SUCCESS == status) {

         status = vxWaitGraph(graph);

    }

    if (VX_SUCCESS == status) {

         status = write_img_to_file("output.img", dst);

    }

    /* ====== 图像处理逻辑结束 ====== */

 

 

    vxReleaseImage(&dst);

    vxReleaseImage(&src);

    vxReleaseGraph(&graph);

    vxReleaseContext(&context);

 

    tivxHostDeInit();

    tivxDeInit();

    return (int)status ;

}

 

 

static vx_status add_rgb2gray_to_graph(vx_graph graph, vx_image src, vx_image dst)

{

    /* 这里没有检测可能存在的错误。 */

    /* 实际上错误会传导的,可以在 vxVerifyGraph() 函数中一并检测。 */

    vx_status status = VX_SUCCESS;

    vx_image nv12 = vxCreateVirtualImage(graph, 0, 0, VX_DF_IMAGE_NV12);

 

    vx_node n0 = vxColorConvertNode(graph, src, nv12);

    vx_node n1 = vxChannelExtractNode(graph, nv12, 0, dst);

 

    vxReleaseImage(&nv12);

    vxReleaseNode(&n0);

    vxReleaseNode(&n1);

    return status;

}

Target 端

/* vx.h 及 tivx.h 这两个头文件是必须要包含的,其它头文件按需包含 */

#include <VX/vx.h>

#include <TI/tivx.h>

 

extern int is_exit;

/* 闲暇任务 */

extern void idle_task(void);

 

int main(int argc, char *argv[])

{

    /* 初始化 Tiovx 框架,包括共享内存初始化、内部日志初始化、创建 IPC 处理线程(或任务)等 */

    /* 根据 Tiovx 库的编译条件,可能还会注册 Openvx 原生 Kernel 在 Target 端的部分。 */

    /* 注:由 Ti 的 RTOS SDK 源码得知,Tiovx 框架下只希望由 DSP 执行节点,不希望 ARM 执行节点。 */

    tivxInit();

 

    /* 由于这里是 Target 端,故不需要调用 tivxHostInit() 函数*/

 

    /* 如有必要,在这里注册自定义 Kernel 在 Target 端的部分。 */

   

 

    /* 防止主线程(或主任务)退出。 */

    /* 如果主线程(或主任务)退出,IPC 处理线程(或任务)也会退出, */

    /* 从而无法处理 Host 端发送的节点创建、执行等命令。 */

    while(is_exit != 1)

    {

        idle_task();

    }

 

    return 0;

}

 

 

 

参考文献链接

https://zhuanlan.zhihu.com/p/476569209

https://software-dl.ti.com/jacinto7/esd/processor-sdk-rtos-jacinto7/latest/exports/docs/tiovx/docs/user_guide/index.html

https://www.ti.com/tool/download/PROCESSOR-SDK-RTOS-J721E

 

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

7~8次题目集总结Blog

一、前言 关于7,8两次家具强电电路模拟程序,我认为是比较困难且综合的程序题,我们需要处理复杂的电路结构,如包含多个并联电路的串联电路,以及并联电路之间的包含关系还要精确地处理输入和输出格式,确保所有的计算和输出都符合题目要求,这些设计程序整体的方面都属于较为…

CentOS7.9部署.NET Core 6.0

简介 本章节主要讲的是在Linux系统CentOS7.9上去完成.NET Core 6.0软件的安装,确定Linux的版本是x64还是arm64的,然后到.NET Core的官网下载6.0的SDK,并进行安装 步骤 1.查看系统版本 2.打开.NET Core 6.0下载网址 3.下载与Linux系统对应版本的.NET Core SDK 4.上传.NET Cor…

全网最适合入门的面向对象编程教程:05 类和对象的Python实现-PyCharm代码标签(一个帮你提升coding效率的小技巧)

本文介绍了PyCharm IDE中代码标签的定义、类型和使用方法。摘要: 本文介绍了PyCharm IDE中代码标签的定义、类型和使用方法。 往期推荐: 学嵌入式的你,还不会面向对象??! 全网最适合入门的面向对象编程教程:00 面向对象设计方法导论 全网最适合入门的面向对象编程教程:…

使用Llama3/Qwen2等开源大模型,部署团队私有化Code Copilot和使用教程

代码辅助工具(Code Copilot)可以提高团队代码研发效率,能有效帮助产品快速上市抢占先发优势,但同时又需要保证代码和信息隐私,私有化Code Copilot是保护隐私的重要手段。老牛通过本文,详细介绍通过开源大模型,部署私有化Code Copilot和使用教程……目前市面上有不少基于…

MySQL共享表空间各个版本之间的演变图

感谢作者! 摘自:https://blog.csdn.net/qq_42267081/article/details/112383883

PTA七、八次总结

前言 本学期的最后两次PTA作业了,毕竟作为压轴的节目,难度还是有的,而且对我来说还不小。这一次Blog也是本学期最后一次Blog了,我也做出了一点对这学期相关PTA作业的总结。 这一学期PTA算得上是一次挣扎中进步的经历了,尤其是这两次。前几次老师给了一些思路给我做出了引导…

大作业7-8总结

前言 这两次大作业使用到的新知识点并不是很多,考查的是各类知识点的综合应用,题量方面大概考虑到这两次大作业的难度和临近考试的学习压力,两次大作业都只有一道大题,由于是所有知识点的总结,考察的知识点很广,再加上迭代的次数增加,这两次大作业的难度比之前的几次都要…

你的第一个SpringMVC程序

1.创建Maven模块 1.1 新建一个父模块1.2修改一下JDK版本这里选择JDK211.3 父模块中建立一个子模块,接下来的操作将在子模块中进行2.添加web支持 2.1设置本地Maven仓库(非必须)2.2 在springmvc-001的pom.xml文件中修改打包方式为war同时添加web所需要的依赖,由于Servlet依赖…

第三次OOP作业总结

第三次OOP作业总结 前言 此次作业为家居强电电路模拟程序的再次迭代,主要增加了两个特殊用电器,并且考察学生前几次的程序架构的完善性。新增的电压与电流的要求迫使程序更加智能。设计与分析 题一:家居强电电路模拟程序-3题解:在器具方面,控制设备增加了互斥开关,受控设…

codis架构学习

转自:https://jackeyzhe.github.io/2018/11/14/玩转Redis集群之Codis/ 1.介绍 codis是一种redis 分布式集群解决方案,codis是基于多个redis实例做了一层路由层来进行数据路由,每个redis实例承担一定的数据分片。Codis FE:集群管理界面。多个集群实例共享可以共享同一个前端…

PTA作业7~8总结

目录1.前言:2.设计与分析:(1)第七次大作业(2)第八次大作业3.采坑心得:4.改进建议:5.总结: ---------------------------------------------------------------------------------------------------------------------------------- PTA作业总结1.前言:这次的作业相比于前几次…

02-单链表的操作

单链表的创建:头插法 #include <stdio.h> #include <malloc.h> #include <stdbool.h>typedef int ElemType; /*定义一个单链表*/ typedef struct LNode{ElemType data;/*下一个元素的地址指针*/struct LNode *next; } LNode,*LinkList;/***单链表的创建* @r…