二、E906移植----项目解读与FPGA基本工程搭建

上篇介绍了基本的环境搭建,并跑了一下官方例程,查看了一下仿真波形。本篇则主要讲一下官方的例程是如何跑通的,涉及到哪些必要步骤,以及对应步骤具体是在干嘛,哪些步骤是可综合的,哪些是必须通过添加额外功能模块才能实现的,最后将在FPGA上部署一个基本的可综合工程,并拉通在FPGA上的项目综合。值得注意的是,本篇结束后搭建的FPGA工程只是包含了项目所需的绝大部分源文件,要实现fpga上基本的跑通还需要进行四五处修改,具体在下一篇介绍。

不同人的了解程度不同,我介绍的是必要的一些流程但不一定够细,所有这些其实都可以通过读源文件来分析出个大概,然后不确定的地方可以多尝试几下。

1. Hello_world 仿真是如何跑起来的?

1.1 可行性

首先评估一下移植的可行性,即 ‘官方的smart_run平台跑通’ —— ‘FPGA能跑通’ 靠谱程度。 因为我们知道qemu的存在,是可以在指令集层面上进行模拟运行环境的,实际上也有很多人做了不少在qemu上模拟riscv运行的有意义的工作,如果这个smart_run是通过qemu来模拟运行的,那这个逆向分析涉及面就可以直接宣布挂机了。

实际上,上一篇博客也大致介绍过,vcs下make rancase后是会生成一个simv的,直接运行这个simv和通过runcase后的打印输出结果一样。那么就可以大概率说明,这个smart_run的命令就是写RTL标准的三板斧:搭建工程RTL源代码 —— 构建testbench,写激励 —— vcs等逻辑仿真工具解释编译生成输出结果文件。

既如此,那我们只需要看看工程到底用了哪些源码,testbench写了哪些激励,然后copy一下源码到FPGA工程,再把testbench里的激励换成实际上FPGA的引脚信号输入,或者PLL时钟输入仿真运行涉及到的所有行为,即可完成移植了,这个难度还是可以接受的。

1.2 项目文件结构简介

opene906-main 2级目录如下
.
├── doc
│ ├── opene906_datasheet.pdf
│ ├── QR_code_openXuantie.png
│ ├── \347\216\204\351\223\201E906\351\233\206\346\210\220\346\211\213\345\206\214.pdf
│ └── \347\216\204\351\223\201E906\347\224\250\346\210\267\346\211\213\345\206\214.pdf
├── E906_RTL_FACTORY
│ ├── gen_rtl
│ └── setup
├── LICENSE
├── README.md
└── smart_run
├── impl
├── logical
├── Makefile
├── setup
├── tests
└── work

其中比较重要的、需要反复查看的:
E906_RTL_FACTORY / gen_rtl ---- 所谓RTL工厂,存放了e906 riscv核心的所有源代码包括:时钟、复位、取指、译码、执行等等基本内核ip源码。

smart_run/logical ---- 一个soc要跑起来光核心不行,还需要外设总线,寄存器/内存模型也得有,基本的GPIO和UART也得有吧,还得有下载调试的模块,另外比较重要的是testbench也在这里,还有一些filelist的汇总。

smart_run/tests ---- 一个cpu要跑起来除了基本的硬件RTL结构,还得需要软件代码才行,该文件目录下就存放了c代码例程以及库文件。

smart_run/work ---- 是自行创建的,用于跑例程仿真的目录,一些中间输出文件包括c代码的编译、汇编、链接输出文件存在于此。

另外,Makefile 也需要反复查看来分析make buildcase/runcase 命令到底具有哪些行为,甚至可以理解为整个FPGA的移植过程其实就是把make runcase命令的仿真行为放到FPGA上来执行而已。 其实在后面也可以看到,如果修改后的文件make runcase没有结果输出,那么同样的一些源文件放到FPGA上也绝大概率挂了。

1.3 Makefile文件大致解读与make runcase运行流程分析

先在后面贴一下smart_run路径下的Makefile,内容略多,但是结构还是比较清晰的。

  1. 上来先引用了setup下的两个文件,其中 smart_cfg.mk 主要包含了调用gcc工具链对c代码项目进行build的命令,env_check.mk则检查 TOOL_EXTENSION 这个工具链路径环境变量是否定义。

  2. 接着是Simulation related 的命令,先检查外部调用时传入的 DUMP和SIM命令字, 特别是根据SIM 传入的值设置不同的编译选项,如熟悉的添加timescale、设置编译支持systemverilog、指定输出log文件等配置。 再下一段有个 SIM_FILELIST 的配置,这个信息比较重要,它告诉我们vcs等仿真器编译时到底调用了哪些文件。 最后就是具体的 compile 和 cleansim 的命令实现。

  3. 再接着就是 Cases related的命令,包括showcase、buildcase、cleancase。showcase就是把 CASE_LIST的值打印出来,CASE_LIST呢则是在引用的smart_cfg.mk中定义的,包含了hello_world等几个可用的测试例程。cleancase就清理一下文件。

    着重看一下buildcase命令的实现,匹配到CASE_LIST后,显示清理了一下,然后调了$(CASE)_build 命令,如果外部输入的是make buildcase CASE=hello_world , 那么实际调用的就是make hello_world_build 命令,这个命令的实现是在smart_cfg.mk中:
    hello_world_build:
    @cp ./tests/cases/hello_world/* ./work
    @find ./tests/lib/ -maxdepth 1 -type f -exec cp {} ./work/ ;
    @cp ./tests/lib/clib/* ./work
    @cp ./tests/lib/newlib_wrap/* ./work
    @cd ./work && make -s clean && make -s all CPU_ARCH_FLAG_0=e906f CPU_ARCH_FLAG_1=nodsp ENDIAN_MODE=little-endian CASENAME=hello_world FILE=hello_world_main >& hello_world_build.case.log
    具体做的就是把hello_world中的文件(只有main函数)、还有相关的c库文件copy到work下,另外tests/lib/下有一个Makefile也被复制了进去,然后就make all。 这个行为可以理解为在keil等CDK中将写好的c代码compile、build的过程。

  4. 最后比较重要的就是这个runcase命令。首先在CASE_LIST中匹配到CASE后,
    首先调用了compile ,也即verilog 硬件描述 RTL的编译;
    接着 buildcase 命令,软件代码综合生成二进制;

    最后视不同的编译器,分别执行生成的文件,vcs 就是./simv,iverilog 就是 vvp xxx.vvp 。

  5. 最后 regress是测试所有案例的,clean 和 help 不多解释。

总之,两点比较关键,一是使用 SIM_FILELIST 中的RTL和tb进行compile,二是对test case进行build。

·····················

1.4 smart_run下的Makefile 文件

#/*Copyright 2020-2021 T-Head Semiconductor Co., Ltd.
#
#Licensed under the Apache License, Version 2.0 (the "License");
#you may not use this file except in compliance with the License.
#You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
#Unless required by applicable law or agreed to in writing, software
#distributed under the License is distributed on an "AS IS" BASIS,
#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#See the License for the specific language governing permissions and
#limitations under the License.
#*/
include ./setup/smart_cfg.mk
include ./setup/env_check.mk
################################################################################
# Simulation related
################################################################################
SIM = iverilog
DUMP = offifeq ($(DUMP), on)
SIM_DUMP :=
else
ifeq (${SIM}, iverilog)
SIM_DUMP = -DNO_DUMP
else
SIM_DUMP := +define+NO_DUMP
endif
endififeq ($(SIM), vcs)
TIMESCALE := -timescale=1ns/100fs
SIMULATOR_OPT := -sverilog -full64 -kdb -lca -debug_access +nospecify +notimingchecks
SIMULATOR_DEF := +define+no_warning +define+TSMC_NO_WARNING
SIMULATOR_LOG := -l comp.vcs.log
else ifeq ($(SIM), nc) TIMESCALE := -timescale 1ns/100fsSIMULATOR_OPT := +v2k -sysv +sv +access+wrc +notimingcheck -default_ext verilog -elaborate +tcl+../setup/nc.tclSIMULATOR_DEF := +define+no_warning +define+TSMC_NO_WARNING +define+VMC +define+NC_SIM SIMULATOR_LOG := -l comp.nc.logelseifeq ($(SIM), iverilog)TIMESCALE := SIMULATOR_OPT := -o xuantie_core.vvp -Diverilog=1 -g2012SIMULATOR_DEF := -DIVERILOG_SIMSIMULATOR_LOG := endifendif
endififeq ($(SIM), iverilog)
SIM_FILELIST := $(SIM_FILELIST) -f ${CODE_BASE_PATH}/gen_rtl/filelists/E906_asic_rtl.fl -f ${CODE_BASE_PATH}/gen_rtl/filelists/tdt_dmi_top_rtl.fl -c ../logical/filelists/smart.fl -c ../logical/filelists/tb.fl
else
SIM_FILELIST := $(SIM_FILELIST) -f ../logical/filelists/sim.fl
endifcompile:@echo "  [THead-smart] Compiling smart now ... "@echo "  [THead-smart] SIM = $(SIM)"
ifeq ($(SIM), vcs)@make -s cleansim@cd ./work && vcs $(SIMULATOR_OPT) $(TIMESCALE) $(SIMULATOR_DEF) $(SIM_FILELIST) $(SIM_DUMP) $(SIMULATOR_LOG)
else ifeq ($(SIM), nc)@cd ./work && irun $(SIMULATOR_OPT) $(TIMESCALE) $(SIMULATOR_DEF) $(SIM_FILELIST) $(SIM_DUMP) $(SIMULATOR_LOG) elseifeq ($(SIM), iverilog)@cd ./work && iverilog $(TIMESCALE) $(SIMULATOR_OPT) $(SIMULATOR_DEF) $(SIM_FILELIST) $(SIM_DUMP) $(SIMULATOR_LOG)else@echo "  [THead-smart] Please specify SIM = vcs to use VCS, or SIM = nc to use irun ..."endifendif
endif cleansim:@cd ./work && rm -rf simv* csrc ucli.key *.vcs.log novas_dump.log *.fsdb
################################################################################
# Cases related
################################################################################
showcase:@echo "  Case lists:" \ @for case in $(CASE_LIST) ; do \echo "    $$case"; \donebuildcase: tool-chain-chk
ifeq ($(CASE),)$(error Please specify CASE=xxx on the command line, like: \$(newline)   > make buildcase CASE=xxx... \$(newline)   The list of valid cases can be obtained by executing: \$(newline)   > make showcase ...)
endif
ifeq ($(findstring $(CASE), $(CASE_LIST)), $(CASE))@make -s cleancase@make -s $(CASE)_build
else$(error Argument CASE=xxx is not valid: \$(newline)   The list of valid cases can be obtained by executing: \$(newline)   > make showcase ...)
endifcleancase:@cd ./work && rm -rf *.s *.S *.c *.o *.pat *.h *.lcf *.hex *.obj *.vh *.v *.report *.elf Makefile *.case.log
################################################################################
# Combined flows
################################################################################
# Execute one case
runcase:
ifeq ($(CASE),)$(error Please specify CASE=xxx on the command line, like: \$(newline)   > make buildcase CASE=xxx... \$(newline)   The list of valid cases can be obtained by executing: \$(newline)   > make showcase ...)
endif
ifeq ($(findstring $(CASE), $(CASE_LIST)), $(CASE))@make -s compile@make -s buildcase CASE=$(CASE)
ifeq ($(SIM), vcs)cd ./work && ./simv -l run.vcs.log
elseifeq ($(SIM), nc)   cd ./work && irun -R -l run.irun.logelsecd ./work && vvp xuantie_core.vvp -l run.iverilog.logendif
endif
else$(error Argument CASE=xxx is not valid: \$(newline)   The list of valid cases can be obtained by executing: \$(newline)   > make showcase ...)
endif# Execute all cases
regress:rm -rf ./tests/regress/regress_result/*@(for case in $(CASE_LIST) ; do \rm -rf ./work/* ; \make -s runcase CASE=$$case; \cp ./work/run_case.report ./tests/regress/regress_result/$$case.report; \done)cd ./tests/regress && perl report_gen.plcat ./tests/regress/regress_report
################################################################################
# Misc
################################################################################
.DEFAULT_GOAL := helpclean:@cd ./work && rm -rf * help:@echo "  ########## Smart Help Info ##########"...

1.5 项目文件结构、testbench 文件分析与移植思路

现在有点方向了,就是把仿真中的SIM_FILELIST中的RTL源文件扔进FPGA工程中,然后把不可综合的激励模块换成板子上的时钟、引脚信号输入。实际的,我们可以先打开仿真中的硬件项目,看一下目录文件组织结构以获取下一步的思路。

①项目文件结构
smart_run 目录下,打开终端,进入csh, 跑一下 make runcase,最后verdi 运行一下仿真输出simv。

[ICer@IC_EDA smart_run]$ csh
[ICer@IC_EDA smart_run]$ make clean
[ICer@IC_EDA smart_run]$ make runcase CASE=hello_world SIM=vcs
[ICer@IC_EDA smart_run]$ ./work/simv -verdi

verdi 界面下工程大体结构:

仿真的顶层文件就是tb,tb里面例化了 soc、uart_mnt、mnt这3个模块;更进一步的x_soc里面还有更多的子级结构,实际上包含了数百个文件。

② tb.v文件
在路径 /opene906-main/smart_run/logical/tb 下存放了仿真运行的顶层文件,先找一找里面仅有的可综合代码:

就是结构中前面提到的 mnt 、 uart_mnt、soc三个模块的例化, 这个tb的所有其他模块应该就是围绕着这3个例化模块来进行信号输入输出操作的,更进一步,soc是整个核心块,uart_mnt其实就是监测soc的uart0_sout输出信号线来模拟uart协议的数据流解析与仿真输出的,mnt 应该是monitor缩写,是用于运行状态监测的。

回过头来,再梳理一遍tb.v文件的流程,重点看一下围绕soc的信号流动方向,尤其是输入信号,因为输出信号的作用多是用于监测,我不监测只是看不到相关信息而已,系统是能运行的(当然$error 等终止语句还是得看看):

i. 先是将模块内部的一些信号定义成宏变量 ;
ii. 然后是关于系统和jtag的时钟与复位信号的激励模块 ;
iii. 接着对memory进行了初始化 ;
iiii. 后面的大都是各种信号监测模块了,但是有个地方需要注意,

$deposit 语句 是具有强制改变模块内部信号量值的作用的,这里大概是和pmu电源管理有关,如进入睡眠、低功耗模式等,需要标记留意一下。 其实后面还有几个force语句,简单分析发现其并没有向soc模块内部传递信号。
iiiii. 其实保险起见,tb内例化的另外两个模块uart_mnt和mnt也可以进去看看信号流向,可以发现均是对x_soc模块相关信号的输出监测,没有对x_soc信号输入。

那么到这里,移植的思路已经很明确了——soc模块就是最核心的模块,把它包含的RTL文件找出来放进fpga中那么整个基本工程就比较完整了;接着是时钟和复位的激励,时钟通过PLL产生,复位接到外部按键;还有一个非常重要的memory初始化,这个其实就是把cpu要运行的指令数据存进memeory中,但是tb的这种写法不可综合,后面的换个方法;最后就是这个pmu的信号控制,以及uart引脚输入输出外接。

2. FPGA基本工程搭建

现在开始搭建FPGA的工程,我这里基于vivado2019.1, 使用的开发板是正点原子的zynq7020板子,如果手里没有开发板子其实也可以先搭建工程跑跑综合、布局看看一看资源占用、时序违例等等方便后面挑选合适的板子,毕竟vivado建立工程的芯片型号是自己随便选的。

2.1 抽取RTL源文件

前面有提到,/opene906-main/E906_RTL_FACTORY/gen_rtl 和 /opene906-main/smart_run/logical 下包含了绝大部分RTL源代码,实际上这里面有3类文件 verilog 文件(xx.v) 、头文件(xx.h)和文件列表(xx.fl),后面看到这里面有几十个文件是多余的,具体哪些多余我们也可以根据Makefile里提到的SIM_FILELIST里的内容去逐个挑选,但是vivado软件是可以自动分析出各个verilog文件之间的包含关系的,所以全部拎出来扔进去就好了,没用到的disable掉就好。

具体的,/opene906-main下新建一个FPGA文件夹,/FPGA 下再新建一个 source文件夹用于存放源代码,然后将/opene906-main/E906_RTL_FACTORY/gen_rtl 和 /opene906-main/smart_run/logical 下文件全部复制进来:


为了更精确点,其实里面的filelists相关文件可以全部删掉,当然不删除后面disable也是可以的。
另外,默认的tb.v是作为top文件的,这个是仿真用的不可综合的,后面必须要把它替换成自己的顶层文件,这里可以先留着用于参考soc模块是如何调用的。

2.2 vivado 下fpga工程创建

  1. /opene906-main/FPGA 下打开终端, 输入vivado 打开,新建工程命名为e906_fpga,添加初始源文件路径-source 至工程: !!!注意,第3步那里最好不要勾选copy into project选项,否则后面单独修改工程内的文件必须去工程路径下慢慢找,不利于统一管理。
  2. 约束文件先不添加,FPGA器件型号我手里是z7020 ,但是E906默认的bram是不够的,可以先选一个资源稍微丰富点的z045,后面工程里面再改回来。
  3. 创建工程后,vivado已经大致帮我们整理好了各个文件之间的包含关系,整体目录结构和仿真过程的./simv -verdi显示得差不多。
  4. 可以看到工程下有58个报错文件,随便点开几个文件看一下,都是提示引用的宏定义量没有定义。此时把Non-module Files下的xxxx.h文件全部设置为全局头文件类型。

    稍作等待后,可以看到只剩下mnt.v 和tb.v两个报错文件。
  5. 因为前面讲了这两个文件和uart_mnt都是用于仿真的,包含了很多不可综合语句,所以我们需要重新编写整个工程的顶层文件。

i. create design file , 命名为e906_top ,只把soc模块例化进去,注意输入输出和inout信号,可以参考下面的。

module e906_top(clk,rst_b,uart0_sin,uart0_sout,b_pad_gpio_porta,jclk,jrst_b,nrst_b,jtg_tdi,jtg_tdo,jtg_tms);input clk;input rst_b;input uart0_sin;output uart0_sout;inout[7:0] b_pad_gpio_porta;input jclk;input jrst_b;input nrst_b;input jtg_tdi;output jtg_tdo;input jtg_tms ;  //instantiate soc
soc u_soc(.i_pad_clk            ( clk                  ),.i_pad_uart0_sin      ( uart0_sin            ),.o_pad_uart0_sout       ( uart0_sout           ),.i_pad_jtg_tclk       ( jclk                 ),.i_pad_jtg_trst_b     ( jrst_b               ),.i_pad_jtg_nrst_b     ( nrst_b               ),.b_pad_gpio_porta     ( b_pad_gpio_porta     ),.i_pad_jtg_tdi        ( jtg_tdi              ),.o_pad_jtg_tdo        ( jtg_tdo              ),.i_pad_jtg_tms        ( jtg_tms              ),   .i_pad_rst_b          ( rst_b                )
);

ii . disable 掉报错的tb 和 mnt模块,uart_mnt也禁能掉,都是仿真用的,其他的pa_spram也可以禁用掉,没报错不禁关系也不大。

iii .将刚才创建的e906_top设置为top,现在就可以跑SYNTHESIS了,注意没添加约束文件还不能跑implemention。

Utilization:

···
···
lut 占用 35.4k , ff 占用14.4k , bram用了384个;
这里也可以看到bram占用有点高,后面换到zynq7020等资源少一点的片子就不够用了,需要对源文件进行相应的修改。

3. 总结

本篇分析了项目的大致运行流程,并最后搭建了E906的基本FGPA工程,用过一些修改配置后完成了synthesis。下一篇将就进一步修改工程来适配具体的板子,并生成比特流下载进板。

二、E906移植----项目解读与FPGA基本工程搭建相关推荐

  1. 商城项目03_人人前后端项目、逆向工程、common工程搭建、coupon以及各个微服务工程搭建

    文章目录 ①. 人人前后端项目 ②. 人人项目-逆向工程 ③. common工程搭建 ④. coupon.member.ware.order代码导入 ①. 人人前后端项目 ①. 在码云上搜索人人开源, ...

  2. 三、E906移植----FPGA生成可用的比特流并实现串口发送

    三.E906移植----FPGA生成可用的比特流并实现串口发送 书接上回,第二篇把基本工程搭建了起来,跑了下综合看了看.本文就开始具体的修改了,连蒙带猜,修修补补,最终完成了板上串口发送"H ...

  3. 嵌入式OS入门笔记-以RTX为案例:二.快速移植到RTX

    嵌入式OS入门笔记-以RTX为案例:二.快速移植到RTX 本篇笔记将简单介绍RTX,包括基本架构,如何在Keil中配置.需要安装ARM-MDK和一块硬件板,笔记以STM32F4Discovery为例子 ...

  4. FPGA之道——FPGA开发流程之项目方案与FPGA设计方案

    文章目录 前言 FPGA开发流程 背景知识的分析与研究 项目方案的设计与制定 写清楚项目背景 写清楚项目需求 写清楚方案框架 写清楚算法细节 确保逻辑完备性 确保实现无关性 确保书面易懂性 算法可行性 ...

  5. 从零开始实现ASP.NET Core MVC的插件式开发(二) - 如何创建项目模板

    标题:从零开始实现ASP.NET Core MVC的插件式开发(二) - 如何创建项目模板 作者:Lamond Lu 地址:https://www.cnblogs.com/lwqlun/p/11155 ...

  6. JavaScript 编程精解 中文第三版 二十一、项目:技能分享网站

    二十一.项目:技能分享网站 原文:Project: Skill-Sharing Website 译者:飞龙 协议:CC BY-NC-SA 4.0 自豪地采用谷歌翻译 部分参考了<JavaScri ...

  7. 模拟数据解决二分类问题项目描述

     模拟数据解决二分类问题项目描述: 已经发布公众号: 利用Python的numpy模块下的random类生成模拟数据集,然后对这些数据进行交叉熵最小化计算找到最合适的参数,也就完成了整个模型的调整 ...

  8. 二代征信报告解读及信贷风控中的应用

    今年一月中旬起,征信中心面向社会公众和金融机构提供二代格式信用报告查询服务,相比一代征信系统,二代征信系统又做了哪些升级?提供的信用报告又有哪些变化?相对于一代征信报告,大家更加急切地想知道二代征信报 ...

  9. 二、软件项目开发计划书-模板

    二.软件项目开发计划书 1.引言 1 1.1编写目的 1 1.2项目背景 1 1.3定义 2 1.4参考资料 2 2.项目概述 2 2.1工作内容 2 2.2条件与限制 2 2.3产品 2 2.4运行 ...

最新文章

  1. matlab定子磁链观测器,一种基于二阶广义积分器的永磁同步电机定子磁链观测方法...
  2. .Net Remoting 1
  3. 红帽虚拟化RHEV-安装RHEV-M
  4. 如何正确对待vb脚本里的session
  5. mysql事务隔离级别与设置
  6. SpringSecurity OAuth2中表结构说明
  7. Ajax的异步,是鸡肋还是鸡排?
  8. 【拿不到offer全额退款】人工智能与 NLP / CV 第三期课程培训招生
  9. python解题教学_PYTHON教学设计:计算机解决问题的过程教案-精.doc
  10. 如何创建SQL Server报告服务(SSRS)报告
  11. 渠道效果五步优化,让采购的流量物超所值
  12. android10解决NetworkInfo废弃替代NetworkInfo isConnected()问题
  13. wincc怎么做数据库_WINCC与数据库连接
  14. 八位全加器——python逻辑电路
  15. python 实时股票行情_python 实时获取股票行情脚本
  16. 一张废手机卡的作用......
  17. 编程题--疯狂序列----京东大数据笔试
  18. 极智Coding | 剖析 darknet load_weights 接口
  19. 网易邮箱大师中添加qq邮箱时,需要开启IMAP,一直卡在验证密保的界面
  20. 谷歌浏览器设置打开新标签页为指定网页?

热门文章

  1. 【预测模型】基于GM(1,1)预测某地区未来6年的人口数量
  2. 前端业内又哪些交流氛围比较好的群、论坛、社区
  3. 什么是Prometheus?
  4. 顺丰控股业绩炸雷:王卫道歉后仍跌跌不休,券商研报继续蒙眼喊单
  5. 荣耀智慧屏鸿蒙系统评测,荣耀智慧屏体验评测:电视只是小功能,鸿蒙系统才是真亮点...
  6. layer-v2.4弹层组件使用示例
  7. python 跨库查询(同一实例不同数据库)和接口数据进行对比,并把结果输出到Excel中
  8. 2022护眼产品展,北京眼健康展,眼科医学展,近视矫正设备展
  9. des_decrypt mysql_MySQL DES_DECRYPT()用法及代码示例
  10. android pms架构图,Android PMS之启动流程