• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# NNRt设备开发指导
2
3## 概述
4
5### 功能简介
6
7NNRt(Neural Network Runtime,神经网络运行时)是面向AI领域的跨芯片推理计算运行时,作为中间桥梁连通上层AI推理框架和底层加速芯片,实现AI模型的跨芯片推理计算。
8
9NNRt开放了设备接口,芯片厂商通过设备接口将专有加速芯片接入NNRt,实现接入OpenHarmony的社区生态,以下将介绍芯片如何接入NNRt。
10
11### 基本概念
12在开发前,开发者需要先了解以下概念,以便更好地理解全文内容:
13
14- HDI(Hardware Device Interface):OpenHarmony硬件设备接口,定义系统中跨进程通信的接口,实现服务间的跨进程通信。
15- IDL(Interface Description Language):接口描述语言,是HDI接口的语言格式。
16
17### 约束与限制
18- 系统版本:OpenHarmony主干版本。
19- 开发环境:Ubuntu 18.04及以上。
20- 接入设备:具备AI计算能力的芯片。
21
22### 运作机制
23NNRt通过HDI接口实现与设备芯片的对接,由HDI接口实现跨进程通信。
24
25**图1** NNRt架构图
26
27![架构图](./figures/nnrt_arch_diagram.png)
28
29整个架构主要分为三层,AI应用在应用层,AI推理框架和NNRt在系统层,设备服务在芯片层。AI应用如要使用专用加速芯片模型推理,需要经过AI推理框架和NNRt才能调用到底层专用加速芯片,而NNRt就是负责适配底层各种专用加速芯片,它开放了标准统一的设备接口,众多的第三方芯片设备都可以通过HDI接口接入OpenHarmony。
30
31程序运行时,AI应用、AI推理框架、NNRt都在用户进程中,底层设备服务在服务进程中,NNRt根据HDI接口实现了HDI Client,服务端也需要根据HDI接口实现HDI Service,使得两个进程间能够跨进程通信。
32
33## 开发指导
34
35### 场景介绍
36当需要将一款AI加速芯片接入NNRt的时候,可以参考下文。下文以RK3568芯片为例,展示RK3568 CPU如何通过HDI接口接入NNRt,并完成AI模型推理。
37> 依赖说明:该教程展示的RK3568 CPU接入NNRt并没有实际去写CPU的驱动,而是借用了MindSpore Lite的CPU算子,故会依赖MindSpore Lite的动态库以及头文件,实际开发时并不需要依赖MindSpore Lite的任何库或者头文件。
38
39### 开发流程
40专用加速芯片接入NNRt的整体流程如下:
41
42**图2** 专用加速芯片接入NNRt流程
43
44![开发流程](./figures/nnrt_dev_flow.png)
45
46### 开发步骤
47开发者具体可通过以下步骤实现芯片对接NNRt:
48#### 生成HDI头文件
49开源社区下载OpenHarmony的代码,编译drivers_interface部件,生成HDI接口的头文件。
50
511. [下载源码](../get-code/sourcecode-acquire.md)。
52
532. 编译接口IDL文件。
54    ```shell
55    ./build.sh --product-name productname –ccache --build-target drivers_interface_nnrt
56    ```
57    > productname为产品名称,此处为RK3568。
58
59    编译完成后,在```out/rk3568/gen/drivers/interface/nnrt```目录下生成HDI头文件,默认语言类型为C++。若需要生成C类型的头文件,请在编译之前使用如下命令对```drivers/interface/nnrt/v1_0/BUILD.gn```文件中的```language```配置项进行设置。
60
61    ```shell
62    language = "c"
63    ```
64
65    生成头文件目录如下所示:
66    ```text
67    out/rk3568/gen/drivers/interface/nnrt
68    └── v1_0
69        ├── drivers_interface_nnrt__libnnrt_proxy_1.0_external_deps_temp.json
70        ├── drivers_interface_nnrt__libnnrt_stub_1.0_external_deps_temp.json
71        ├── innrt_device.h                        # 设备接口头文件
72        ├── iprepared_model.h                     # 编译AI模型对象头文件
73        ├── libnnrt_proxy_1.0__notice.d
74        ├── libnnrt_stub_1.0__notice.d
75        ├── model_types.cpp                       # AI模型结构定义实现文件
76        ├── model_types.h                         # AI模型结构定义头文件
77        ├── nnrt_device_driver.cpp                # 设备驱动实现参考样例
78        ├── nnrt_device_proxy.cpp
79        ├── nnrt_device_proxy.h
80        ├── nnrt_device_service.cpp               # 设备服务端实现参考样例
81        ├── nnrt_device_service.h                 # 设备服务端头文件
82        ├── nnrt_device_stub.cpp
83        ├── nnrt_device_stub.h
84        ├── nnrt_types.cpp                        # 数据类型定义实现文件
85        ├── nnrt_types.h                          # 数据类型定义头文件
86        ├── node_attr_types.cpp                   # AI模型算子属性定义实现文件
87        ├── node_attr_types.h                     # AI模型算子属性定义
88        ├── prepared_model_proxy.cpp
89        ├── prepared_model_proxy.h
90        ├── prepared_model_service.cpp            # 编译AI模型对象服务端实现参考样例
91        ├── prepared_model_service.h              # 编译AI模型对象服务端头文件
92        ├── prepared_model_stub.cpp
93        └── prepared_model_stub.h
94    ```
95
96#### 实现HDI服务
97
981. 在drivers/peripheral目录下新建开发目录,用于HDI服务开发,开发目录结构如下所示:
99    ```text
100    drivers/peripheral/nnrt
101    ├── BUILD.gn                                  # 代码编译脚本文件
102    ├── bundle.json
103    └── hdi_cpu_service                           # 自定义目录
104        ├── BUILD.gn                              # 代码编译脚本文件
105        ├── include
106        │   ├── nnrt_device_service.h             # 设备服务端头文件
107        │   ├── node_functions.h                  # 非必须,由具体实现决定
108        │   ├── node_registry.h                   # 非必须,由具体实现决定
109        │   └── prepared_model_service.h          # 编译AI模型对象服务端头文件
110        └── src
111            ├── nnrt_device_driver.cpp            # 设备驱动实现文件
112            ├── nnrt_device_service.cpp           # 设备服务端实现文件
113            ├── nnrt_device_stub.cpp              # 非必须,由具体实现决定
114            ├── node_attr_types.cpp               # 非必须,由具体实现决定
115            ├── node_functions.cpp                # 非必须,由具体实现决定
116            ├── node_registry.cpp                 # 非必须,由具体实现决定
117            └── prepared_model_service.cpp        # 编译AI模型对象服务端实现文件
118    ```
119
1202. 实现设备驱动,无特殊需求可直接使用步骤1中生成的nnrt_device_driver.cpp文件,否则根据具体驱动开发。
121
1223. 实现服务接口,可参考nnrt_device_service.cppprepared_model_service.cpp实现文件,接口定义可以参考[NNRt的HDI接口定义](https://gitee.com/openharmony/drivers_interface/tree/master/nnrt)123
1244. 编译驱动和服务的实现文件为共享库。
125
126    在```drivers/peripheral/nnrt/hdi_cpu_service/```下新建```BUILD.gn```文件,文件内容如下所示,相关参数配置内容可参考[Build教程](https://gitee.com/openharmony/build)127
128    ```shell
129    import("//build/ohos.gni")
130    import("//drivers/hdf_core/adapter/uhdf2/uhdf.gni")
131
132    ohos_shared_library("libnnrt_service_1.0") {
133      include_dirs = []
134      sources = [
135        "src/nnrt_device_service.cpp",
136        "src/prepared_model_service.cpp",
137        "src/node_registry.cpp",
138        "src/node_functions.cpp",
139        "src/node_attr_types.cpp"
140      ]
141      public_deps = [ "//drivers/interface/nnrt/v1_0:nnrt_idl_headers" ]
142      external_deps = [
143        "hdf_core:libhdf_utils",
144        "hiviewdfx_hilog_native:libhilog",
145        "ipc:ipc_single",
146        "c_utils:utils",
147      ]
148
149      install_images = [ chipset_base_dir ]
150      subsystem_name = "hdf"
151      part_name = "drivers_peripheral_nnrt"
152    }
153
154    ohos_shared_library("libnnrt_driver") {
155      include_dirs = []
156      sources = [ "src/nnr_device_driver.cpp" ]
157      deps = [ "//drivers/peripheral/nnrt/hdi_cpu_service:libnnrt_service_1.0" ]
158
159      external_deps = [
160        "hdf_core:libhdf_host",
161        "hdf_core:libhdf_ipc_adapter",
162        "hdf_core:libhdf_utils",
163        "hiviewdfx_hilog_native:libhilog",
164        "ipc:ipc_single",
165        "c_utils:utils",
166      ]
167
168      install_images = [ chipset_base_dir ]
169      subsystem_name = "hdf"
170      part_name = "drivers_peripheral_nnrt"
171    }
172
173    group("hdf_nnrt_service") {
174      deps = [
175        ":libnnrt_driver",
176        ":libnnrt_service_1.0",
177      ]
178    }
179    ```
180
181    将```group("hdf_nnrt_service")```添加到```drivers/peripheral/nnrt/BUILD.gn```文件中,以便在更上目录层级就能引用。
182    ```shell
183    if (defined(ohos_lite)) {
184      group("nnrt_entry") {
185        deps = [ ]
186      }
187    } else {
188      group("nnrt_entry") {
189        deps = [
190          "./hdi_cpu_service:hdf_nnrt_service",
191        ]
192      }
193    }
194    ```
195
196    新建```drivers/peripheral/nnrt/bundle.json```用于定义新增的```drivers_peripheral_nnrt```部件。
197    ```json
198    {
199      "name": "drivers_peripheral_nnrt",
200      "description": "Neural network runtime device driver",
201      "version": "3.2",
202      "license": "Apache License 2.0",
203      "component": {
204        "name": "drivers_peripheral_nnrt",
205        "subsystem": "hdf",
206        "syscap": [""],
207        "adapter_system_type": ["standard"],
208        "rom": "1024KB",
209        "ram": "2048KB",
210        "deps": {
211          "components": [
212            "ipc",
213            "hdf_core",
214            "hiviewdfx_hilog_native",
215            "c_utils"
216          ],
217          "third_part": [
218            "bounds_checking_function"
219          ]
220        },
221        "build": {
222          "sub_component": [
223            "//drivers/peripheral/nnrt:nnrt_entry"
224          ],
225          "test": [
226          ],
227          "inner_kits": [
228          ]
229        }
230      }
231    }
232    ```
233
234#### 声明HDI服务
235
236  在对应产品的uhdf hcs配置文件中声明用户态驱动与服务,例如针对RK3568服务需要在```vendor/hihope/rk3568/hdf_config/uhdf/device_info.hcs```文件中新增如下配置:
237  ```text
238  nnrt :: host {
239      hostName = "nnrt_host";
240      priority = 50;
241      uid = "";
242      gid = "";
243      caps = ["DAC_OVERRIDE", "DAC_READ_SEARCH"];
244      nnrt_device :: device {
245          device0 :: deviceNode {
246              policy = 2;
247              priority = 100;
248              moduleName = "libnnrt_driver.z.so";
249              serviceName = "nnrt_device_service";
250          }
251      }
252  }
253  ```
254> 注意:修改hcs文件需要删除out目录重新编译,才能生效。
255
256#### 配置host进程用户ID和组ID
257  对于新增的nnrt_host进程的场景,需要配置对应进程的用户ID和组ID。 进程的用户ID在文件```base/startup/init/services/etc/passwd```中配置,进程的组ID在文件```base/startup/init/services/etc/group```中配置。
258  ```text
259  # 在base/startup/init/services/etc/passwd新增
260  nnrt_host:x:3311:3311:::/bin/false
261
262  # 在base/startup/init/services/etc/group新增
263  nnrt_host:x:3311:
264  ```
265
266#### 配置SELinux
267
268OpenHarmony已经开启SELinux特性,需要对新增的进程和服务配置相应的SELinux规则,用于运行host进程启动访问某些资源、发布HDI服务。
269
2701. 在```base/security/selinux/sepolicy/ohos_policy/drivers/adapter/vendor/type.te```文件中配置nnrt_host进程安全上下文,新增配置如下:
271    ```text
272    # 新增配置
273    type nnrt_host, hdfdomain, domain;
274    ```
275    > nnrt_host为上文配置的进程名称。
276
2772. 由于SeLinux是白名单访问的权限机制,需要根据实际权限需求配置。将服务启动之后,可通过以下dmesg命令查看avc告警,
278avc告警会给出缺少的权限。SeLinux的配置也可以参考[OpenHarmony SeLinux子系统的说明](https://gitee.com/openharmony/security_selinux/blob/master/README.md)279    ```shell
280    hdc_std shell
281    dmesg | grep nnrt
282    ```
283
2843. 新建nnrt_host.te配置文件,将权限配置到nnrt_host.te文件中。
285    ```shell
286    # 创建nnrt文件夹
287    mkdir base/security/selinux/sepolicy/ohos_policy/drivers/peripheral/nnrt
288
289    # 创建vendor文件夹
290    mkdir base/security/selinux/sepolicy/ohos_policy/drivers/peripheral/nnrt/vendor
291
292    # 创建nnrt_host.te文件
293    touch base/security/selinux/sepolicy/ohos_policy/drivers/peripheral/nnrt/vendor/nnrt_host.te
294    ```
295
2964. 将所需的权限写入nnrt_host.te文件中,比如:
297    ```text
298    allow nnrt_host dev_hdf_kevent:chr_file { ioctl };
299    allow nnrt_host hilog_param:file { read };
300    allow nnrt_host sh:binder { transfer };
301    allow nnrt_host dev_ashmem_file:chr_file { open };
302    allow sh nnrt_host:fd { use };
303    ```
304
305#### 配置部件编译入口
306以RK3568产品为例:
307```shell
308vim //productdefine/common/inherit/chipset_common.json
309```
310在"subsystems", "subsystem":"hdf", "components"中添加:
311```shell
312{
313  "component": "drivers_peripheral_foo",
314  "features": []
315}
316```
317
318#### 删除out目录并编译整个系统
319```shell
320# 删除out目录
321rm -rf ./out
322
323# 编译
324./build.sh --product-name rk3568 –ccache --jobs=4
325```
326
327
328### 调测验证
329服务开发完成后,可以使用XTS用例验证基本功能和兼容性,开发者可通过以下步骤进行验证:
330
3311. 编译NNRt的hats用例,用例在```test/xts/hats/hdf/nnrt```目录下。
332    ```shell
333    # 进入hats目录
334    cd test/xts/hats
335
336    # 编译hats测试用例
337    ./build.sh suite=hats system_size=standard --product-name rk3568
338
339    # 回到代码根目录
340    cd -
341    ```
342    编译好的测试用例会输出到相对代码根目录的```out/rk3568/suites/hats/testcases/HatsHdfNnrtFunctionTest```路径下。
343
3442. 将测试用例push到设备上。
345    ```shell
346    # 将测试用例可执行文件推送到设备上,HatsHdfNnrtFunctionTest是测试用例可执行文件。
347    hdc_std file send out/rk3568/suites/hats/testcases/HartsHdfNnrtFunctionTest /data/local/tmp/
348
349    # 给测试用例可执行文件加上权限。
350    hdc_std shell "chmod +x /data/local/tmp/HatsHdfNnrtFunctionTest"
351    ```
352
3533. 执行用例并查看结果。
354    ```shell
355    # 执行测试用例
356    hdc_std shell "/data/local/tmp/HatsHdfNnrtFunctionTest"
357    ```
358
359    所有hats用例执行成功,可以看到测试报告显示已通过47个用例,说明服务通过了兼容性测试。
360    ```text
361    ...
362    [----------] Global test environment tear-down
363    Gtest xml output finished
364    [==========] 47 tests from 3 test suites ran. (515 ms total)
365    [  PASSED  ] 47 tests.
366    ```
367
368### 开发实例
369完整Demo代码可以参考[社区实现](https://gitee.com/openharmony/ai_neural_network_runtime/tree/master/example/drivers)370
3711. 拷贝```example/driver/nnrt```目录到```drivers/peripheral```路径下。
372    ```shell
373    cp -r example/driver/nnrt drivers/peripheral
374    ```
375
3762. 补充bundle.json文件到```drivers/peripheral/nnrt```,bundle.json参考本教程上面的[开发步骤](#开发步骤)章节。
377
3783. 由于Demo依赖MindSpore Lite CPU算子,故需要添加MindSpore Lite依赖文件。
379    - 下载MindSpore Lite的头文件,[MindSpore Lite 1.5.0](https://ms-release.obs.cn-north-4.myhuaweicloud.com/1.5.0/MindSpore/lite/release/linux/mindspore-lite-1.5.0-linux-x64.tar.gz)380    - 在```drivers/peripheral/nnrt```目录下新建mindspore目录,用于存放mindspore依赖库和头文件。
381      ```shell
382      mkdir drivers/peripheral/nnrt/mindspore
383      ```
384    - 解压mindspore-lite-1.5.0-linux-x64.tar.gz文件,将```runtime/include```目录拷贝到```drivers/peripheral/nnrt/mindspore```目录下。
385    - Demo还依赖mindspore的schema文件。
386      ```shell
387      # 创建mindspore_schema目录
388      mkdir drivers/peripheral/nnrt/hdi_cpu_service/include/mindspore_schema
389
390      # 拷贝mindspore schema文件
391      cp third_party/mindspore/mindspore/lite/schema/* drivers/peripheral/nnrt/hdi_cpu_service/include/mindspore_schema/
392      ```
393    - 编译MindSpore Lite的动态库,并将动态库放到mindspore目录下。
394      ```shell
395      # 编译mindspore动态库
396      ./build.sh --product-name rk3568 -ccaache --jobs 4 --build-target mindspore_lib
397
398      # 将mindspore动态库
399      mkdir drivers/peripheral/nnrt/mindspore/mindspore
400
401      # 将mindspore动态拷贝到drivers/peripheral/nnrt/mindspore/mindspore402      cp out/rk3568/package/phone/system/lib/libmindspore-lite.huawei.so drivers/peripheral/nnrt/mindspore/mindspore/
403      ```
4044. 其他配置请参考本教程上面的[开发步骤](#开发步骤)章节。