本文采用知识共享 署名-非商业性使用-禁止演绎 4.0 国际协议授权(CC BY-NC-ND 4.0),转载请注明出处.
Preface
本文将简要介绍映射nvdla nv_small版本到FPGA过程中可能遇到的‘坑’,旨在给做相同工作的小伙伴一个参考,不作指导性建议.
本文会尽可能完整地描述从官方源码构建vivado IP工程到最终BD工程网表生成的全部步骤以及每个步骤中容易出错的地方. 但需要说明的是,该映射工作是本人公司项目的一部分,所以,受规则所限,文中不会直接展示官方开源内容以外的设计内容,望谅解.
Development environment setup
- Win10: build vivado project for HW
- Vmware Ubuntu 16.04: host for petalinux project
- Vivado: 2017.4
-
Petalinux: 2017.4, Linux kernel v4.9.
(不同版本
petalinux的linux kernel版本不同,从而DMA API不同,导致对nvdla/sw/kmd驱动源码的修改方式有些区别) - Board: Xilinx zcu102 rev1.0
Step 1: Build Tree and vmod (Linux)
官方HW工程是通过在branch/spec/defs/下定义不同的spec对同一源码构建不同的行为模型架构,所以,源码内部有很多的c++和perl相关的条件编译. 因此,在搭建vivado工程前,要先按照官方NVDLA Environment Setup Guide将nv_small/vmod/nvdla编译为纯RTL source code.
====Tips====
1). build tree所需的工具中,cpp,gcc,g++,perl,python采用linux系统默认,java需要额外安装,若只是
build RTL,其他tool可直接enter.
2). 在build RTL过程还需要perl module的支持,可以直接使用类似pip的perl CPAN安装错误信息中指定的module,
如
$ perl -MCPAN -e shell
...
CPAN > install YAML
...
CPAN > exit
成功[TMAKE]:DONE后,在目录下会生成一个outdir目录,目录下便是搭建vivado所需的全部RTL code.
Step 2: Create RTL Project (Win10)
1.添加源文件,可按照nv_small/spec/defs/nv_small.spec描述来指定vmod/nvdla/目录下待添加的内核源文件————small版本不包含bdma,retiming和rubik内核(文件夹)以及其他文件夹中的部分.v————实际中,可先添加top文件下的modules. 之后,vivado显示的hierarchy中缺少什么文件,加之即可;
2.添加RAM文件时,要选择outdir/nv_small/vmod/rams/fpga/small_rams/文件夹下的RAM源文件;
====Tips====
1). Xilinx RAM资源的使用有4种基本方式————flexibility依次递减————i)源码推断, ii)XPMs, iii)直接例化
8k/16k RAM primitives, iv)例化RAM IP.
2). 待全部缺失文件补全后,可能会发现在NV_nvdla hierarchy以外,还存在一些modules,这个是因为一个文
件中定义了多个modules,可以将未用到的modules comment掉.
3.关闭clock gating,原设计对RAM存储op使用了大量clock gating以降低功耗,但与processor,ASIC不同,FPGA的时钟树是设计好的,clock buf资源有限,若不关闭gating,可能产生很大skew(之前因为部分gating未关闭,测试一直不过). 使用到的clock gating开关宏包括以下4个,有4种设置方式:i)在vivado的Project Settings->General->Verilog options->Defines中添加宏名称和值;ii)定义.vh头文件,define宏,右键该头文件选择Global Include;iii)定义.vh,之后在vivado的Project Settings->General->Verilog options->Verilog Include Files Search Paths中选择头文件的路径;iv)前三种方式都不需要将头文件在各源文件中include,最后一种是笨方法,即将头文件include到所有相关的.v源文件中,大家自行选择.
- VLIB_BYPASS_POWER_CG
- NV_FPGA_FIFOGEN
- FIFOGEN_MASTER_CLK_GATING_DISABLED
- FPGA
- SYNTHESIS
====Tips====
1). 实际上,也可以不关闭gating信号逻辑,可使用syth选项支持相关逻辑的clock gating,clk buf会增加, 作者测试了
部分信号, 待测试全部信号的资源消耗和逻辑稳定性.
2). nvdla_pwrbus_ram_*_pd相关逻辑,可以转化使用BRAM的sleep达到相同效果,待测试.
4.[optional] 之后可在NV_nvdla里添加generated时钟,进行综合及布局布线查看资源开销、功耗和时钟频率等信息.
====Tips====
之前的描述可能对一些小伙伴产生了误导,导致几个人都mail我问综合之后IO资源超标,无法进行PR的问题.
这里需要解释一下,整个nv_small加速核与外部有两个通信接口,AXI总线接口用于与DDR控制器交互读写数据,
APB接口与MCU交互配置nvdla内部各子核的寄存器,前者的接口数量很大,后者接口只有少量几个,如果直
接综合或PR,相当于将这些接口连接到了FPGA的引脚上,所以IO的资源会超标,这里有两种基本解决方式,
i)使用内置处理器的MPSoC FPGA芯片,这样处于PL部分的nvdla接口将直接连接到PS部分的MCU上,并由
MCU与DDR控制器通信读写片外DDR;ii)选择IO引脚数量大的FPGA芯片,通过AXI chip-to-chip与MCU通信.
Step 3: Packaging NVDLA IP (Win10)
1.添加wrapper,如果在NV_nvdla里例化了generated clock,请删除,另外,为了在block design中连接PS的AXI master和AXI slave接口,需要在当前工程结构下,增加一个NV_nvdla_wrapper module封装NV_nvdla和NV_NVDLA_apb2csb modules;
====Tips====
也可以将axi apb bridge IP同时封装在wrapper中,这样在BD工程中,PS->nvdla IP方向通信就变成了
axi master->axi slave接口互连映射,而非本文采用的axi master->axi apb bridge->apb slave互连模式.
2.补全AXI与APB信号,NVDLA使用AXI和APB协议与PS通信,但源码中缺失了部分AXI协议信号,需按照AMBA AXI and ACE Protocol Spec和AMBA 3 APB Protocol spec补全缺失的AXI, APB信号,即
//append axi signal to NV_nvdla_wrapper signal list
output [2 : 0] M_AXI_AWSIZE,
output [1 : 0] M_AXI_AWBURST,
output M_AXI_AWLOCK,
output [3 : 0] M_AXI_AWCACHE,
output [2 : 0] M_AXI_AWPROT,
output [3 : 0] M_AXI_AWQOS,
output M_AXI_AWUSER,
output M_AXI_WUSER,
input [1 : 0] M_AXI_BRESP,
input M_AXI_BUSER,
output [2 : 0] M_AXI_ARSIZE,
output [1 : 0] M_AXI_ARBURST,
output M_AXI_ARLOCK,
output [3 : 0] M_AXI_ARCACHE,
output [2 : 0] M_AXI_ARPROT,
output [3 : 0] M_AXI_ARQOS,
output M_AXI_ARUSER,
input [1 : 0] M_AXI_RRESP,
input M_AXI_RUSER
//append apb signal to NV_nvdla_wrapper signal list
output S_APB_PSLVERR,
并将相应信号分别添加到NV_nvdla和NV_NVDLA_apb2csb的信号列表中,并分别添加如下赋值代码,
//add these code to NV_nvdla module
assign m_axi_awsize = 3;
assign m_axi_awburst = 2'b01;
assign m_axi_awlock = 1'b0;
assign m_axi_awcache = 4'b0010;
assign m_axi_awprot = 3'h0;
assign m_axi_awqos = 4'h0;
assign m_axi_awuser = 'b1;
assign m_axi_wuser = 'b0;
assign m_axi_arsize = 3;
assign m_axi_arburst = 2'b01;
assign m_axi_arlock = 1'b0;
assign m_axi_arcache = 4'b0010;
assign m_axi_arprot = 3'h0;
assign m_axi_arqos = 4'h0;
assign m_axi_aruser = 'b1;
//add below code to NV_NVDLA_apb2csb module
assign pslverr = 1'b0;
====Tips====
添加上述信号后,其实已经能够在BD工程中完成interface连接任务了,但是仅作上述修改,BD工程中必须要使用AXI
smartconnect IP做PL与PS的接口互连,如果要使用AXI interconnect IP或干脆直接将NVDLA master与PS slave互连,
就要进一步修改如下信号位宽, 否则,BD工程在validate时会报错.
//modify following signal bit width in NV_nvdla module
//input [7:0] nvdla_core2dbb_b_bid;
input [5:0] nvdla_core2dbb_b_bid;
//input [7:0] nvdla_core2dbb_r_rid;
input [5:0] nvdla_core2dbb_r_rid;
//output [7:0] nvdla_core2dbb_aw_awid;
output [5:0] nvdla_core2dbb_aw_awid;
//output [3:0] nvdla_core2dbb_aw_awlen;
output [7:0] nvdla_core2dbb_aw_awlen;
//output [7:0] nvdla_core2dbb_ar_arid;
output [5:0] nvdla_core2dbb_ar_arid;
//output [3:0] nvdla_core2dbb_ar_arlen;
output [7:0] nvdla_core2dbb_ar_arlen;
实际上,所有的*_id signal只有后4位work. 另外,不要忘了在NV_nvdla module的NV_NVDLA_partition_o u_partition_o{...}实例中修改相应信号位宽,这里就不写了.
3.添加xdc,新建两个xdc文件,一个为nvdla IP在OOC综合时使用,另一个则是在global syth时使用,OOC xdc可以直接约束两个primary clk,如
create_clock -period 10.001 -name u_dla_core_clk [get_ports u_dla_core_clk];
create_clock -period 10.001 -name u_dla_sys_clk [get_ports u_dla_sys_clk];
另一个xdc保持空白即可,这里涉及到了一个xdc scope问题,感兴趣的可以参考xilinx UG903. 除此之外,在source窗口选中OOC版本的xdc,并在属性窗口中,为USED_IN属性添加out-of-context选项,否则,BD综合时将多出两个主时钟. 选中另一版本xdc,在属性窗口中为PROCESSING_ORDER属性选择LATE, 这样,如果后期在该约束文件中为nvdla IP添加新约束,那么BD工程综合时不会与全局约束冲突.
4.封装nvdla IP,Tools-->Create and Package New IP-->Package your current project, next and finish;
5.AXI master interface推断,点击Ports and Interfaces,如Fig-1, 查看信号列表中是否存在自动推断出的AXI master接口,若没有,则在信号列表中选中全部master接口信号,右键选择Auto Infer Interface,推断出master接口信号后,需要检查位宽是否与源文件中声明的相一致(之前出现过标量矢量化的情况,工具坑),即Size Left,Size Right. 另外,检查Driver Value,如Fig-2, 若官方源码中声明的信号在此列表中显示驱动强度为0,则选中该信号,在属性窗口删除0值,另外,需要对上面我们后添加的master信号,将驱动强度设置为0;
6.APB slave interface推断,这个接口工具不会自动推断,需要选中全部APB信号,右键自动推断,在弹出窗口中依次选择Advanced–>apb_rtl. 在推断出AXI和APB信号后,分别右键两个接口信号,选择Associate Clocks,分别关联AXI master–>*_core_clk和APB slave–>*_csb_clk;
7.APB memory map, AXI/APB master-slave接口是通过memory-map机制做数据映射的,作者画了一个映射结构简图Fig-3. 不同于AXI master memory block的自动生成. APB memory block需要自行添加,选择Addressing and Memory–>Memory Maps(for slaves),右键IP Addressing and Memory Wizard, 弹出窗口中选择APB接口信号,继续右键Add Address Block(因为一块连续地址,一个block便可),弹出窗口键入reg,如Fig-4;
8.Review and Package –>Package IP.
Step 4: Create Block Design Project (Win10)
1.新建RTL工程,Settings–>IP–>Repository将刚刚封装的nvdla IP (nvdla_ip_prj_name.srcs)添加到IP列表,新建BD工程, Flow Navigator–>Create Block Design;
2.添加ps,nvdla ip,axi apb bridge,axi interconnect等IP,ps要配置AXI master,AXI slave以及pl_ps_irq中断接口,之后连接接口即可. 对于nvdla ip的几个接口信号————global_clk_ovr_on, tmc2slcg_disable_clock_gating, test_mode,nvdla_pwrbus_ram_*_pd按照官网small版本Integrator’s Manual建议,使用Constant ip直接拉低,BD工程结构如Fig-5;
3.地址分配,待interface连接完成后,切换到Address Editor页面,右键选择Auto Assign Address对互连接口进行地址映射,slave接口保留64k即可(官网已指明small版本的Address space layout所占空间为56KB,所以这里分配64k足矣). 之后切换回Diagram窗口,validate.
4.添加xdc,综合、布局布线和输出bit文件,之后export hardware(复选include bitstream).
Step 5: [optional] Run Test Sets
Vivado+SDK/VIP/HW manager, 请自行选择,我没做
.