NVDLA软件架构和源码解析 第一章—内核驱动【华为云技术分享】
驱动整体设计介绍
不同的processor
Nvidia DLA的内核驱动KMD(Kernel mode driver)中,并不是把DLA当成一个设备来控制,而是把不同的功能模块当做不同的processor,分别进行任务的管理和控制。在相同processor里分先后,不同队列靠依赖关系控制。当前分了6个processor,代码中如下定义。
#define DLA_OP_BDMA 0
#define DLA_OP_CONV 1
#define DLA_OP_SDP 2
#define DLA_OP_PDP 3
#define DLA_OP_CDP 4
#define DLA_OP_RUBIK 5
对应的芯片架构如下:
Headless控制模式
架构上分为高端的headed和低端的headless方案。差别就是是否有MCU来承担IPU的逻辑控制功能。如果有host CPU和MCU两种角色,就是headed方案,否则就是headless。
NVDLA的初始开源版本将仅提供headless模式的软件解决方案。本次运行的场景是在linux下,但是是按照CPU直接操控DLA的寄存器来实现的,也就是headless模式。好处是很容易从无头模式看到核心的IPU控制逻辑。
主要的流程思路
除了初始化,驱动主要处理对上对下两个接口。对上是来和用户态的IOCTL,对下是处理中断。两者最终都是调用或者激活nvdal_task_submit,清理task之间的dependency关系,将合适的task放入processor执行。
图表 1 KMD的主流程
特殊的group寄存器
每个子模块processor有两套寄存器,也就是2个group寄存器。这种乒乓思路类似于队列和shadow寄存器的意图,方便一个任务完成后紧接着处理另外一个group寄存器配置的任务,而不是需要完成产生中断后软件才配置下一个任务,以此保持硬件逻辑的全速运行。
软件里定义如下:
/** * @ingroup Processors * @name Number of groups * @brief Each processor has 2 groups of registers * @{ */ #define DLA_NUM_GROUPS 2 struct dla_processor_group { uint8_t id; uint8_t rdma_id; uint8_t active; uint8_t events; uint8_t roi_index; uint8_t is_rdma_needed; uint8_t pending; int32_t lut_index; uint8_t programming; uint64_t start_time; struct dla_common_op_desc *op_desc; struct dla_common_op_desc *consumers[DLA_OP_NUM]; struct dla_common_op_desc *fused_parent; union dla_operation_container *operation_desc; union dla_surface_container *surface_desc; };
驱动的注册和初始化
NVDLA驱动以Platform device driver的形式注册到内核。依靠name = "NVDLA”来进行match。重要的函数为nvdla_probe。
module_platform_driver(nvdla_driver); static struct platform_driver nvdla_driver = { .probe = nvdla_probe, .remove = __exit_p(nvdla_remove), .driver = { .owner = THIS_MODULE, .name = "NVDLA", .of_match_table = nvdla_of_match, }, };
Probe函数nvdla_probe
Step1:
申请nvdla_dev并使用platform_set_drvdata挂载到如参pdev上。
Step2:
用devm_ioremap_resource映射内存,用devm_request_irq注册中断。
Step3:
调用dla_register_driver将全局变量engine关联到nvdla_dev->engine_context,Engine包含了device的各种能力,如DMA, CONV。这个就说明engine的含义是各个DLA中模块独立的处理能力。
把desc_cache和desc_refcount设置全0。最后调用nvdla_drm_probe注册了drm(Direct Rendering Manager)驱动设备。Nvdla使用drm的主要目的是利用了drm的内存分配机制和ioctl。其实和drm的功能没什么关系。Nvdia作为GPU厂商,借用drm更轻车熟路。
中断处理
中断处理nvdla_engine_isr
Step1
dla_isr_handler(nvdla_dev->engine_context);
主要是根据读取S_INTR_STATUS寄存器得到的值,设置对应engine->processors的group->events标记。比如如果是GLB_S_INTR_STATUS_0就设置engine->processors[DLA_OP_CONV].groups[0].events= (1 << DLA_EVENT_OP_COMPLETED)
step2
complete(&nvdla_dev->event_notifier);
这个会唤醒nvdla_task_submit,调用dla_process_events来处理event。因为中断里可能表示有任务完成,那么就可能消除dependency,可能放入新的任务执行。
对用户态的接口nvdla_submit利用了drm框架的ioctl。
struct drm_driver nvdla_drm_driver里包含了nvdla_drm_ioctls。索引到nvdla_submit。其他几个ioctl是内存相关的。
DRM_IOCTL_DEF_DRV(NVDLA_SUBMIT, nvdla_submit, DRM_RENDER_ALLOW),
1. 用copy_from_user从ioctl接口复制一些用户态的task数据到内核。
2. 调用nvdla_fill_task_desc填充task结构体
3. 调用nvdla_task_submit。
主流程函数讲解
Step1
调用dla_execute_task
Step2
等待wait_for_completion(&nvdla_dev->event_notifier),然后调用dla_process_events。
dla_process_events和dla_handle_events
dla_process_events遍历DLA_OP_NUM个processor调用dla_handle_events(processor)。
dla_handle_events遍历自己的processor->groups读取信息,先处理dma相关的event,调用dla_update_consumers来解除dependency_count。如果发现有operation的dependency_count降为0,就dla_enable_operation。这就等于是收到中断后,遍历所有类型的processor,遍历所有的group来解除dependency,找到能执行的下一个任务。
dla_handle_events最后调用了dla_op_completion来处理complete event。
1. 先调用dla_update_consumers清理dependency。
2. 如果(engine->network->num_operations == engine->num_proc_hwl)说明网络都算完了。如果还有任务接着调用dla_program_operation和dla_enable_operation来执行下一个任务,和dla_submit_operation的思路差不多。
/** * Execute task selected by task scheduler * * 1. Read network configuration for the task * 2. Initiate processors with head of list for same op * 3. Start processing events received */
Step1
dla_read_network_config:
1. 将网络的信息读取到network全局变量。注意这里通过DMA直接读取network descriptor的结构体,不是逐个成员复制。
2. 用DMA将operation descriptor,dependency读取过来。
3. 用DMA读取surface descriptor,LUT,ROI的信息。
Step2
dla_initiate_processors
1. 遍历DLA_OP_NUM调用dla_get_op_desc找到类型中第一个任务,调用dla_submit_operation提交。
2. 调用dla_put_op_desc。将提交后的dla_common_op_desc consumer在desc_refcount对应的count--,如果为0就删除。
3. 调用dla_dequeue_operation将同类型的下一个操作从取出来,使用的是dla_get_op_desc来取。Dequeue的含义就是取下一个,似乎用fetch更合适。如果有下个op需要执行就调用dla_submit_operation继续执行,否则返回。但是dla_dequeue_operation调用dla_submit_operation并不查看返回结果,所以相当于有空闲资源有任务就继续提交,没有就算了。
dla_get_op_desc
1. 在desc_cache中找到当前task,以(desc->index == index && desc->roi_index == roi_index)判定相同。desc_refcount++
2. 如果没找到,在desc_cache取一个新entry,设置好,读取该task的对应的dependency_graph_addr的内容,设置desc_refcount为1。
dla_submit_operation
1. 调用dla_prepare_operation。看看有没有空闲的processor group,将任务描述放入空闲processor的group。没有空闲就返回错误。
2. 如果processor->is_ready,如果没有ready,就退出。
3. 如果ready,调用dla_program_operation。这里就开始设置对应操作类型的processor的寄存器了。也就是提交任务给硬件了。
4. 如果(op_desc->dependency_count == 0),就enable任务。这个问说明在上一步只是把所有运行的配置做好。任务是否能运行,dependency的关系是在AP的driver里完成的,而不是让DLA硬件来完成。另外,在任务的配置过程中,AP并不是通过某种命令发给DLA,而是DLA的各个子模块处理的processor的控制细节寄存器完全暴露出来,由AP直接配置各寄存器。
HDC.Cloud 华为开发者大会2020 即将于2020年2月11日-12日在深圳举办,是一线开发者学习实践鲲鹏通用计算、昇腾AI计算、数据库、区块链、云原生、5G等ICT开放能力的最佳舞台。
NVDLA软件架构和源码解析 第一章—内核驱动【华为云技术分享】的更多相关文章
- Dubbo原理和源码解析之服务引用
一.框架设计 在官方<Dubbo 开发指南>框架设计部分,给出了引用服务时序图: 另外,在官方<Dubbo 用户指南>集群容错部分,给出了服务引用的各功能组件关系图: 本文将根 ...
- Dubbo原理和源码解析之“微内核+插件”机制
github新增仓库 "dubbo-read"(点此查看),集合所有<Dubbo原理和源码解析>系列文章,后续将继续补充该系列,同时将针对Dubbo所做的功能扩展也进行 ...
- Go语言备忘录:net/http包的使用模式和源码解析
本文是晚辈对net/http包的一点浅显的理解,文中如有错误的地方请前辈们指出,以免误导! 转摘本文也请注明出处:Go语言备忘录:net/http包的使用模式和源码解析,多谢! 目录: 一.http ...
- Dubbo原理和源码解析之标签解析
一.Dubbo 配置方式 Dubbo 支持多种配置方式: XML 配置:基于 Spring 的 Schema 和 XML 扩展机制实现 属性配置:加载 classpath 根目录下的 dubbo.pr ...
- Dubbo原理和源码解析之服务暴露
github新增仓库 "dubbo-read"(点此查看),集合所有<Dubbo原理和源码解析>系列文章,后续将继续补充该系列,同时将针对Dubbo所做的功能扩展也进行 ...
- Go语言备忘录(3):net/http包的使用模式和源码解析
本文是晚辈对net/http包的一点浅显的理解,文中如有错误的地方请前辈们指出,以免误导! 转摘本文也请注明出处:Go语言备忘录(3):net/http包的使用模式和源码解析,多谢! 目录: 一.h ...
- Spring源码解析02:Spring IOC容器之XmlBeanFactory启动流程分析和源码解析
一. 前言 Spring容器主要分为两类BeanFactory和ApplicationContext,后者是基于前者的功能扩展,也就是一个基础容器和一个高级容器的区别.本篇就以BeanFactory基 ...
- Spring源码解析 | 第二篇:Spring IOC容器之XmlBeanFactory启动流程分析和源码解析
一. 前言 Spring容器主要分为两类BeanFactory和ApplicationContext,后者是基于前者的功能扩展,也就是一个基础容器和一个高级容器的区别.本篇就以BeanFactory基 ...
- rest-framework之视图和源码解析
视图和源码解析 通过使用mixin类编写视图: from rest_framework import mixins from rest_framework import generics class ...
随机推荐
- 使用Keepalived实现MySQL双主高可用
MySQL双主配置 环境准备: OS: CentOS7 master:192.168.1.10 backup:192.168.1.20 VIP:192.168.1.30 一.安装MySQL数据库. 在 ...
- 【学习笔记】PYTHON语言程序设计(北理工 嵩天)
1 Python基本语法元素 1.1 程序设计基本方法 计算机发展历史上最重要的预测法则 摩尔定律:单位面积集成电路上可容纳晶体管数量约2年翻倍 cpu/gpu.内存.硬盘.电子产品价格等都遵 ...
- 交叉编译用于生成aarch64指令的GCC (9.2)
参考 Building GCC as a cross compiler for Raspberry Pi How to Build a GCC Cross-Compiler 环境 PC: ubuntu ...
- <人人都懂设计模式>-中介模式
真正的用房屋中介来作例子, 好的书籍总是让人记忆深刻. class HouseInfo: def __init__(self, area, price, has_window, has_bathroo ...
- WPF的依赖属性和附加属性(用法解释较全)
转:https://www.cnblogs.com/zhili/p/WPFDependencyProperty.html 一.引言 感觉最近都颓废了,好久没有学习写博文了,出于负罪感,今天强烈逼迫自己 ...
- 02-docker入门-docker常用的一些命令
在这里,有必要先对ducker在做一次介绍 ducker 是一个容器. 容器内部运行的是一个系统. 系统内部安装好了要调试 / 发布的工程,然后这个系统被打包成了一个镜像. ducker 就是这个镜像 ...
- 《浅谈我眼中的express、koa和koa2》好文留存+笔记
原文 :三英战豪强,思绪走四方.浅谈我眼中的express.koa和koa2 一.回调大坑怎么解决呢? 1.es5可以利用一下第三方库,例如 async 库, 2.或者单纯使用 connect中间件 ...
- spark基础知识一
1. spark是什么 Apache Spark™ is a unified analytics engine for large-scale data processing. spark是针对于大规 ...
- 第08组 Beta版本演示
简介 组名:955 组长博客:点这里! 成员 031702329庄锡荣(组长) 031702309林晓锋 031702309侯雅倩 031702311陈珊珊 171709030吴珂雨 03170231 ...
- [算法模版]AC自动机
[算法模版]AC自动机 基础内容 板子不再赘述,OI-WIKI有详细讲解. \(query\)函数则是遍历文本串的所有位置,在文本串的每个位置都沿着\(fail\)跳到根,将沿途所有元素答案++.意义 ...