1# Neural Network Runtime设备接入指导 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 28 29整个架构主要分为三层,AI应用在应用层,AI推理框架和NNRt在系统层,设备服务在芯片层。AI应用如果要使用AI专用加速芯片进行模型推理,需要经过AI推理框架和NNRt才能调用到底层AI专用加速芯片,NNRt就是负责适配底层各种AI专用加速芯片的中间层。NNRt开放了标准统一的HDI设备接口,众多AI专用加速芯片都可以通过HDI接口接入OpenHarmony。此外NNRt也开放了标准统一的接口对接上层各种AI推理框架。 30 31程序运行时,AI应用、AI推理框架、NNRt都运行在用户进程中,底层AI芯片设备服务运行在HDI服务进程中。NNRt根据HDI接口实现了HDI Client,服务端也需要根据HDI接口实现HDI Service,两者通过OpenHarmony标准的HDF子系统实现跨进程通信。 32 33## 开发指导 34 35### 场景介绍 36当需要将一款AI加速芯片接入NNRt的时候,可以参考下文。下文以RK3568芯片为例,展示RK3568 CPU如何通过NNRt的V2.0版本HDI接口接入NNRt,并完成AI模型推理。V1.0版本HDI接口接入NNRt的流程与此类似。 37> 依赖说明:该教程展示的RK3568 CPU接入NNRt并没有真正实现CPU的驱动,而是借用了MindSpore Lite的runtime和CPU算子,因此会依赖MindSpore Lite的动态库以及头文件。实际开发时并不需要依赖MindSpore Lite的任何库或者头文件。 38 39### 开发流程 40AI专用加速芯片接入NNRt的整体流程如下: 41 42**图2** AI专用加速芯片接入NNRt流程 43 44 45 46### 开发步骤 47AI芯片设备HDI服务开发者具体可通过以下步骤实现AI专用加速芯片对接NNRt: 48#### 生成HDI头文件 49开源社区下载OpenHarmony的代码,编译drivers_interface部件,生成HDI接口的头文件。 50 511. [下载源码](../get-code/sourcecode-acquire.md)。 52 532. 进入OpenHarmony源码根目录,编译NNRt的IDL接口文件(以RK3568产品为例): 54 ```shell 55 ./build.sh --product-name rk3568 –ccache --build-target drivers_interface_nnrt 56 ``` 57 58 编译完成后,会在```out/rk3568/gen/drivers/interface/nnrt/v2_0```目录下生成C++类型的HDI头文件。若需要生成C类型的头文件,请在编译之前使用如下命令对```drivers/interface/nnrt/v2_0/BUILD.gn```文件中的```language```配置项进行设置。 59 60 ```shell 61 language = "c" 62 ``` 63 64 生成头文件目录如下所示: 65 ```text 66 out/rk3568/gen/drivers/interface/nnrt 67 └── v2_0 68 ├── drivers_interface_nnrt__libnnrt_proxy_2.0_external_deps_temp.json 69 ├── drivers_interface_nnrt__libnnrt_stub_2.0_external_deps_temp.json 70 ├── innrt_device.h # 设备接口头文件 71 ├── iprepared_model.h # 编译AI模型对象头文件 72 ├── libnnrt_proxy_2.0__notice.d 73 ├── libnnrt_stub_2.0__notice.d 74 ├── model_types.cpp # AI模型结构定义实现文件 75 ├── model_types.h # AI模型结构定义头文件 76 ├── nnrt_device_driver.cpp # 设备驱动实现参考样例 77 ├── nnrt_device_proxy.cpp 78 ├── nnrt_device_proxy.h 79 ├── nnrt_device_service.cpp # 设备服务端实现参考样例 80 ├── nnrt_device_service.h # 设备服务端头文件 81 ├── nnrt_device_stub.cpp 82 ├── nnrt_device_stub.h 83 ├── nnrt_types.cpp # 数据类型定义实现文件 84 ├── nnrt_types.h # 数据类型定义头文件 85 ├── node_attr_types.cpp # AI模型算子属性定义实现文件 86 ├── node_attr_types.h # AI模型算子属性定义 87 ├── prepared_model_proxy.cpp 88 ├── prepared_model_proxy.h 89 ├── prepared_model_service.cpp # 编译AI模型对象服务端实现参考样例 90 ├── prepared_model_service.h # 编译AI模型对象服务端头文件 91 ├── prepared_model_stub.cpp 92 └── prepared_model_stub.h 93 ``` 94 95#### 实现HDI服务 96 971. 进入OpenHarmony源码根目录,在```drivers/peripheral```目录下新建开发目录```nnrt```,用于HDI服务开发。开发目录结构如下所示: 98 ```text 99 drivers/peripheral/nnrt 100 ├── bundle.json 101 ├── v2_0 102 ├── BUILD.gn # 代码编译脚本文件 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. 实现设备驱动,无特殊需求可直接使用IDL文件编译生成的```nnrt_device_driver.cpp```文件,否则根据具体驱动开发。 121 1223. 实现服务接口,可参考```nnrt_device_service.cpp```和```prepared_model_service.cpp```实现文件,接口定义可以参考[NNRt的HDI接口定义](https://gitee.com/openharmony/drivers_interface/tree/master/nnrt)。 123 1244. 编译驱动和服务的实现文件为共享库。 125 126 在```drivers/peripheral/nnrt/v2_0/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_2.0") { 133 include_dirs = [] 134 sources = [ 135 "src/nnrt_device_service.cpp", 136 "src/node_functions.cpp", 137 "src/node_registry.cpp", 138 "src/prepared_model_service.cpp", 139 "src/shared_buffer_parser.cpp", 140 "src/validation.cpp", 141 ] 142 143 external_deps = [ 144 "c_utils:utils", 145 "drivers_interface_nnrt:libnnrt_stub_2.0", 146 "hdf_core:libhdf_utils", 147 "hilog_native:libhilog", 148 "ipc:ipc_core", 149 ] 150 151 install_images = [ chipset_base_dir ] 152 subsystem_name = "hdf" 153 part_name = "drivers_peripheral_nnrt" 154 } 155 156 ohos_shared_library("libnnrt_driver") { 157 include_dirs = [] 158 sources = [ "src/nnr_device_driver.cpp" ] 159 deps = [ ":libnnrt_service_2.0" ] 160 161 external_deps = [ 162 "c_utils:utils", 163 "drivers_interface_nnrt:libnnrt_stub_2.0", 164 "hdf_core:libhdf_host", 165 "hdf_core:libhdf_ipc_adapter", 166 "hdf_core:libhdf_utils", 167 "hdf_core:libhdi", 168 "hilog_native:libhilog", 169 "ipc:ipc_core", 170 ] 171 172 install_images = [ chipset_base_dir ] 173 subsystem_name = "hdf" 174 part_name = "drivers_peripheral_nnrt" 175 } 176 177 group("hdf_nnrt_service") { 178 deps = [ 179 ":libnnrt_driver", 180 ":libnnrt_service_2.0", 181 ] 182 } 183 ``` 184 185 将```group("hdf_nnrt_service")```添加到```drivers/peripheral/nnrt/v2_0/BUILD.gn```文件中,以便在更上目录层级就能引用。 186 ```shell 187 if (defined(ohos_lite)) { 188 group("nnrt_entry") { 189 deps = [] 190 } 191 } else { 192 group("nnrt_entry") { 193 deps = [ "./hdi_cpu_service:hdf_nnrt_service" ] 194 } 195 } 196 ``` 197 198 新建```drivers/peripheral/nnrt/bundle.json```用于定义新增的```drivers_peripheral_nnrt```部件。 199 ```json 200 { 201 "name": "drivers_peripheral_nnrt", 202 "description": "Neural network runtime device driver", 203 "version": "4.0", 204 "license": "Apache License 2.0", 205 "component": { 206 "name": "drivers_peripheral_nnrt", 207 "subsystem": "hdf", 208 "syscap": [""], 209 "adapter_system_type": ["standard"], 210 "rom": "1024KB", 211 "ram": "2048KB", 212 "deps": { 213 "components": [ 214 "c_utils", 215 "hdf_core", 216 "hilog_native", 217 "ipc" 218 ], 219 "third_part": [ 220 "bounds_checking_function" 221 ] 222 }, 223 "build": { 224 "sub_component": [ 225 "//drivers/peripheral/nnrt/v2_0:nnrt_entry" 226 ], 227 "test": [ 228 ], 229 "inner_kits": [ 230 ] 231 } 232 } 233 } 234 ``` 235 236#### 声明HDI服务 237 238 在对应产品的uhdf hcs配置文件中声明NNRt的用户态驱动与服务。例如针对RK3568,服务需要在```vendor/hihope/rk3568/hdf_config/uhdf/device_info.hcs```文件中新增如下配置: 239 ```text 240 nnrt :: host { 241 hostName = "nnrt_host"; 242 priority = 50; 243 uid = ""; 244 gid = ""; 245 caps = ["DAC_OVERRIDE", "DAC_READ_SEARCH"]; 246 nnrt_device :: device { 247 device0 :: deviceNode { 248 policy = 2; 249 priority = 100; 250 moduleName = "libnnrt_driver.z.so"; 251 serviceName = "nnrt_device_service"; 252 } 253 } 254 } 255 ``` 256> 注意:修改hcs文件需要删除out目录重新编译,才能生效。 257 258#### 配置host进程用户ID和组ID 259 对于新增的nnrt_host进程的场景,需要配置对应进程的用户ID和组ID。 进程的用户ID在文件```base/startup/init/services/etc/passwd```中配置,进程的组ID在文件```base/startup/init/services/etc/group```中配置。 260 ```text 261 # 在base/startup/init/services/etc/passwd新增 262 nnrt_host:x:3311:3311:::/bin/false 263 264 # 在base/startup/init/services/etc/group新增 265 nnrt_host:x:3311: 266 ``` 267 268#### 配置SELinux 269 270OpenHarmony已经开启SELinux特性,需要对新增的进程和服务配置相应的SELinux规则,用于运行host进程访问某些资源、发布HDI服务等。 271 2721. 在```base/security/selinux/sepolicy/ohos_policy/drivers/adapter/public/hdf_service_contexts```文件中新增配置: 273 ```text 274 # 新增配置 275 nnrt_device_service u:object_r:hdf_nnrt_device_service:s0 276 ``` 277 > ```nnrt_host```为[声明HDI服务](#声明hdi服务)步骤中配置的进程名称,下同。 278 2792. 在```base/security/selinux/sepolicy/ohos_policy/drivers/adapter/public/hdf_service.te```文件中新增配置: 280 ```text 281 # 新增配置 282 type hdf_nnrt_device_service, hdf_service_attr; 283 ``` 284 2853. 在```base/security/selinux/sepolicy/ohos_policy/drivers/adapter/public/hdfdomain.te```文件中新增配置: 286 ```text 287 # 新增配置 288 neverallow { domain -hdfdomain -sadomain } { hdfdomain -nnrt_host -allocator_host -hdf_public_domain }:binder call; 289 ``` 290 2914. 在```base/security/selinux/sepolicy/ohos_policy/drivers/adapter/public/type.te```文件中新增配置: 292 ```text 293 # 新增配置 294 type nnrt_host, hdfdomain, domain; 295 ``` 296 2975. 在```base/security/selinux/sepolicy/ohos_policy/drivers/adapter/vendor/hdf_devmgr.te```文件中新增配置: 298 ```text 299 # 新增配置 300 allow hdf_devmgr nnrt_host:binder { call transfer }; 301 allow hdf_devmgr nnrt_host:dir { search }; 302 allow hdf_devmgr nnrt_host:file { open read write }; 303 allow hdf_devmgr nnrt_host:process { getattr }; 304 ``` 305 3066. 在```base/security/selinux/sepolicy/ohos_policy/drivers/adapter/vendor/init.te```文件中新增配置: 307 ```text 308 # 新增配置 309 allow init nnrt_host:process { rlimitinh siginh transition }; 310 ``` 311 3127. 在```base/security/selinux/sepolicy/ohos_policy/startup/init/public/chipset_init.te```文件中作如下修改: 313 314 找到chipset_init这一行: 315 ```text 316 allow chipset_init { light_host input_user_host wifi_host camera_host power_host audio_host }:process { rlimitinh siginh transition }; 317 ``` 318 在host列表中增加nnrt_host: 319 ```text 320 allow chipset_init { light_host input_user_host wifi_host camera_host power_host audio_host nnrt_host }:process { rlimitinh siginh transition }; 321 ``` 322 3238. 新建```nnrt_host.te```配置文件: 324 ```shell 325 # 创建nnrt文件夹 326 mkdir base/security/selinux/sepolicy/ohos_policy/drivers/peripheral/nnrt 327 328 # 创建vendor文件夹 329 mkdir base/security/selinux/sepolicy/ohos_policy/drivers/peripheral/nnrt/vendor 330 331 # 创建nnrt_host.te文件 332 touch base/security/selinux/sepolicy/ohos_policy/drivers/peripheral/nnrt/vendor/nnrt_host.te 333 ``` 334 3359. 将所需的权限写入```nnrt_host.te```文件中: 336 ```text 337 allow nnrt_host dev_hdf_kevent:chr_file { ioctl }; 338 allow nnrt_host hilog_param:file { read open map }; 339 allow nnrt_host sh:binder { transfer }; 340 allow nnrt_host samgr:binder { call }; 341 allow nnrt_host dev_ashmem_file:chr_file { open }; 342 allow nnrt_host dev_unix_socket:dir { search }; 343 allow nnrt_host hdf_device_manager:hdf_devmgr_class { get }; 344 allow nnrt_host hdf_nnrt_device_service:hdf_devmgr_class { add get }; 345 allow nnrt_host dev_console_file:chr_file { read write }; 346 allow nnrt_host debug_param:file { read open map }; 347 allow nnrt_host sa_device_service_manager:samgr_class { get }; 348 allow nnrt_host hdf_devmgr:binder { call transfer }; 349 allow nnrt_host hdf_nnrt_device_service:binder { call }; 350 allow nnrt_host sysfs_devices_system_cpu:file { read open getattr }; 351 allow sh hdf_nnrt_device_service:hdf_devmgr_class { add get }; 352 allow sh hdf_hci_interface_service:hdf_devmgr_class { get }; 353 allow sh nnrt_host:dir { getattr search }; 354 allow sh nnrt_host:file { open read }; 355 allow sh nnrt_host:process { getattr }; 356 allow sh nnrt_host:binder { call }; 357 allow sh nnrt_host:fd { use }; 358 ``` 359 36010. 由于SELinux是白名单访问的权限机制,需要根据实际权限需求配置。将服务启动之后,可通过以下dmesg命令查看avc告警, 361avc告警会给出缺少的权限。SELinux的配置也可以参考[OpenHarmony SELinux子系统的说明](https://gitee.com/openharmony/security_selinux/blob/master/README.md)。 362 ```shell 363 hdc_std shell 364 dmesg | grep nnrt 365 ``` 366 367#### 配置部件编译入口 368以RK3568产品为例: 369```shell 370vim //productdefine/common/inherit/chipset_common.json 371``` 372在```"subsystems"```, ```"subsystem":"hdf"```, ```"components"```中添加: 373```shell 374{ 375 "component": "drivers_peripheral_nnrt", 376 "features": [] 377} 378``` 379 380#### 删除out目录并编译整个系统 381```shell 382# 删除out目录 383rm -rf ./out 384 385# 编译 386./build.sh --product-name rk3568 –ccache --jobs=4 387``` 388 389 390### 调测验证 391服务开发完成后,可以使用XTS用例验证基本功能和兼容性。开发者可通过以下步骤进行验证: 392 3931. 编译NNRt的hats用例,用例在```test/xts/hats/ai/nnrt/hdi```目录下。 394 ```shell 395 # 进入hats目录 396 cd test/xts/hats 397 398 # 编译hats测试用例 399 ./build.sh suite=hats system_size=standard product_name=rk3568 400 401 # 回到代码根目录 402 cd - 403 ``` 404 编译好的测试用例可执行文件```HatsHdfNnrtFunctionTest```会输出到```out/rk3568/suites/hats/testcases/```下。 405 4062. 将测试用例push到RK3568设备的```/data/local/tmp/```目录下。 407 ```shell 408 # 将测试用例可执行文件推送到设备上,HatsHdfNnrtFunctionTest是测试用例可执行文件。 409 hdc_std file send out/rk3568/suites/hats/testcases/HartsHdfNnrtFunctionTest /data/local/tmp/ 410 411 # 给测试用例可执行文件加上权限。 412 hdc_std shell "chmod +x /data/local/tmp/HatsHdfNnrtFunctionTest" 413 ``` 414 4153. 执行用例并查看结果。 416 ```shell 417 # 执行测试用例 418 hdc_std shell "/data/local/tmp/HatsHdfNnrtFunctionTest" 419 ``` 420 421 测试报告显示已通过47个用例,说明所有hats用例已执行成功,服务已通过兼容性测试。 422 ```text 423 ... 424 [----------] Global test environment tear-down 425 Gtest xml output finished 426 [==========] 47 tests from 3 test suites ran. (515 ms total) 427 [ PASSED ] 47 tests. 428 ``` 429 430### 开发实例 431完整Demo代码可以参考[社区实现](https://gitee.com/openharmony/ai_neural_network_runtime/tree/master/example/drivers)。 432 4331. 进入OpenHarmony源码根目录,在```drivers/peripheral```路径下创建```nnrt```目录,拷贝NNRt源码路径```foundation/ai/neural_network_runtime```下的```example/driver/nnrt/v2_0```目录到```drivers/peripheral/nnrt```路径下。 434 ```shell 435 cp -r example/drivers/nnrt/v2_0 drivers/peripheral/nnrt 436 ``` 437 4382. 在```drivers/peripheral/nnrt```下补充```bundle.json```文件,```bundle.json```的写法参考本教程上面[开发步骤](#开发步骤)中的[实现HDI服务](#实现hdi服务)章节。 439 4403. 由于Demo依赖MindSpore Lite CPU算子,因此需要添加MindSpore Lite依赖文件: 441 - 在OpenHarmony源码根目录执行以下命令编译MindSpore Lite动态库。MindSpore源码在OpenHarmony源码根目录third_party/mindspore位置下。 442 ```shell 443 # 编译mindspore动态库 444 ./build.sh --product-name rk3568 -ccaache --jobs 4 --build-target mindspore_lib 445 ``` 446 - 在```drivers/peripheral/nnrt/v2_0```下创建```mindspore```目录,用于存放mindspore动态库和头文件。 447 ```shell 448 mkdir drivers/peripheral/nnrt/v2_0/mindspore 449 ``` 450 - 将MindSpore源码中```mindspore-src/source/include```目录拷贝到```drivers/peripheral/nnrt/v2_0/mindspore```目录下。 451 ```shell 452 cp third_party/mindspore/mindspore-src/source/include drivers/peripheral/nnrt/v2_0/mindspore 453 ``` 454 - Demo还依赖mindspore的```schema```文件: 455 ```shell 456 # 创建mindspore_schema目录 457 mkdir drivers/peripheral/nnrt/v2_0/hdi_cpu_service/include/mindspore_schema 458 459 # 从third_party目录拷贝mindspore schema文件 460 cp third_party/mindspore/mindspore-src/source/mindspore/lite/schema/* drivers/peripheral/nnrt/v2_0/hdi_cpu_service/include/mindspore_schema/ 461 ``` 462 - 将编译好的OpenHarmony的MindSpore Lite动态库拷贝到```mindspore```目录下。 463 ```shell 464 # 在drivers/peripheral/nnrt/v2_0/mindspore下创建mindspore目录 465 mkdir drivers/peripheral/nnrt/v2_0/mindspore/mindspore 466 467 # 从out目录将mindspore动态库拷贝到drivers/peripheral/nnrt/v2_0/mindspore/mindspore下 468 cp out/rk3568/package/phone/system/lib/libmindspore-lite.so drivers/peripheral/nnrt/v2_0/mindspore/mindspore/ 469 ``` 4704. 其他配置请参考本教程上面的[开发步骤](#开发步骤)章节。 471