• Home
Name Date Size #Lines LOC

..--

nnrt/12-May-2024-4,2963,296

README_zh.mdD12-May-202416.5 KiB382323

README_zh.md

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