详细教程:vivado2019.2 & vitis2019.2下,zynq7000系列FPGA固化PL程序到外挂flash和SD卡
0.简介
xilinx的zynq系列FPGA既包含了传统的FPGA部分,又嵌入了arm的硬核,分别称为PL(Programmable Logic)和PS (Processing System)。PL部分以前在ISE,现在在vivado上编程配置,包括通用的组合逻辑资源、软IP、硬IP核(包括arm核的基本配置与调用在内),使用语言就是硬件描述语言、原理图文件、IP配置;而PS部分的编程是在vitis或者sdk上进行(2019版本后sdk被升级成vitis)。一般下载软件,vivado和vitis是配套的,一起安装,分开使用。
zynq仅仅使用PL部分固化程序到非易失性存储器(外挂flash芯片、SD卡 )也需要引入arm硬核,步骤繁琐,前不久会做没多久就有些模糊了,而且其他教程要么不全面,要么是针对sdk而非vitis的,导致浪费大量时间,因此记录一下供后续查阅参考使用。
总体必要流程包括:建立PL工程 – 添加以及配置arm核 – 创建vitis工程、生成固化的bin文件 – 下载到flash或拷贝到sd
1.建立PL项目
1.1 首先打开vivado2019 创建PL工程
1.2 创建时如果是移植已有工程,可以直接在这一步添加源文件或源文件夹,约束文件在下一步添加,也可以后面创建好后添加;没有就跳过,由于我是演示,这里就直接添加。注意:可以添加HDL硬件描述语言文件、bd原理图、ip核,直接选中对应文件夹进行,但是rom ip的初始数据文件是不会被加进去的,需要后面再手动添加进去。
1.3 添加约束文件,包括基本的引脚约束,以及相应的时序约束,可以添加多个文件,不同文件中不同的内容会合并,重复冲突的内容会被排在后面的覆盖,而且约束文件只对顶层文件有效。
1.4 选择芯片型号
1.5 等待工程新建完成,vivado会自动根据源文件的调用关系,建立子逻辑。可以看到相应的PL部分约束文件、源文件(包括IP也被添加进来,再强调一遍,选中IP源文件夹即可,前工程综合时不能使用global的ip生成方式),而且自动建立顶层与子模块关系。
1.6 调整工程文件、IP核配置。例如,Coefficient Files 里那个文件是标红的,说明有问题,这是因为我添加了一个rom IP,这个在前工程里加载了这个初始数据文件,但是新工程下路径变了,文件就找不到了。注意移植rom时都需要重新加载一下初始文件,主要是选一下路径。
1.7 PL工程准备完毕,直接生成bitstream,下载并验证,确保PL部分功能正常,也确保下载器和器件正常,这里留意一下最终生成的PL工程消耗的资源情况。
2. vivado内添加ARM核,配置,生成顶层管理文件,并导出hardware文件。
2.1 前面完成了PL部分的建立,要完成固化还必须添加arm硬核才行,通过原理图方式添加arm硬核比较简单。这里新建一个block design的bd文件。
2.2 此时工程除了前面的PL文件,还有新建的block design文件。bd 文件中添加ZYNQ的IP核,也就是PS部分,包含arm核及uart、ddr等相关控制资源。
双击选中后,如下:
2.3 双击并配置ZYNQ核:
2.3.1 添加内容,需要flash外挂芯片的就勾选QSPI flash,需要SD卡的就勾选SD卡,不勾对应的程序固化就不能用。
①QSPI flash控制器:切换到Peripheral I/O Pins – > 勾选Quad SPI Flash,引脚复用根据实际硬件板子情况选择
②SD卡控制器: 切换到Peripheral I/O Pins – > 勾选SD,引脚复用根据实际硬件板子情况选择
③ UART: Peripheral I/O Pins – > 勾选相应UART引脚。
2.3.2 修改内容:
①Bank1电压设置成 LVCMOS1.8V, 根据实际硬件bank电压,我手里是正点原子的板子。
②DDR内容配置修改,主要是选型,注意根据实际情况选兼容的型号:
2.3.3删除部分内容:由于没有用到PS的相应功能,很多引脚都可以去掉,只保留FIXED_IO和DDR相关引脚即可完成下载固化过程,甚至有教程说DDR都可以取消。
①M_AXI_GP0_ACLK 和 M_AXI_GP0引脚取消勾选:
②FCLK_RESET0_N取消勾选:
③FCLK_CLK0引脚取消勾选:
2.4 ZYNQ内部功能配置完成,确定并退出到bd文件中继续配置,可以看到引脚只有两组,这足够我们进行程序固化了,因或者说其他引脚在固化程序上是多余的。
2.5 点击Run Block Automation,自动生成引脚。
2.6 IP核配置设计初步检查
2.7 block design 文件生成output products,这实际上是先综合ZYNQ这个IP核。
2.8 此时工程内有部分源文件,即原PL部分和新建的zynq 发block design文件。然后,在zynq的block design文件上右键,生成HDL Wrapper,此时block design被例化进Wrapper文件中去。
2.9 把原PL顶层文件也例化进生成的wapper.v文件中去,看似复杂,实际就是添加几个引脚然后例化一下。注意例化时的端口引脚名称要和引脚约束文件中的名称一致,建议复制原PL引脚名称。
module ps_downloadtest_wrapper
(DDR_addr,
DDR_ba,
DDR_cas_n,
DDR_ck_n,
DDR_ck_p,
DDR_cke,
DDR_cs_n,
DDR_dm,
DDR_dq,
DDR_dqs_n,
DDR_dqs_p,
DDR_odt,
DDR_ras_n,
DDR_reset_n,
DDR_we_n,
FIXED_IO_ddr_vrn,
FIXED_IO_ddr_vrp,
FIXED_IO_mio,
FIXED_IO_ps_clk,
FIXED_IO_ps_porb,
FIXED_IO_ps_srstb,
///自行添加的PL部分引脚
sys_clk ,
sys_rst_n ,
led ,
lcd_hs ,
lcd_vs ,
lcd_de ,
lcd_rgb ,
lcd_bl ,
lcd_clk ,
uart_rxd ,
uart_txd ,
dds_out
);
//自行添加的PL部分引脚
input sys_clk ;
input sys_rst_n ;
output [1:0] led ;
output lcd_hs ;
output lcd_vs ;
output lcd_de ;
inout [23:0] lcd_rgb ;
output lcd_bl ;
output lcd_clk ;
input uart_rxd ;
output uart_txd ;
output dds_out ;
inout [14:0]DDR_addr;
inout [2:0]DDR_ba;
inout DDR_cas_n;
inout DDR_ck_n;
inout DDR_ck_p;
inout DDR_cke;
inout DDR_cs_n;
inout [3:0]DDR_dm;
inout [31:0]DDR_dq;
inout [3:0]DDR_dqs_n;
inout [3:0]DDR_dqs_p;
inout DDR_odt;
inout DDR_ras_n;
inout DDR_reset_n;
inout DDR_we_n;
inout FIXED_IO_ddr_vrn;
inout FIXED_IO_ddr_vrp;
inout [53:0]FIXED_IO_mio;
inout FIXED_IO_ps_clk;
inout FIXED_IO_ps_porb;
inout FIXED_IO_ps_srstb;
wire [14:0]DDR_addr;
wire [2:0]DDR_ba;
wire DDR_cas_n;
wire DDR_ck_n;
wire DDR_ck_p;
wire DDR_cke;
wire DDR_cs_n;
wire [3:0]DDR_dm;
wire [31:0]DDR_dq;
wire [3:0]DDR_dqs_n;
wire [3:0]DDR_dqs_p;
wire DDR_odt;
wire DDR_ras_n;
wire DDR_reset_n;
wire DDR_we_n;
wire FIXED_IO_ddr_vrn;
wire FIXED_IO_ddr_vrp;
wire [53:0]FIXED_IO_mio;
wire FIXED_IO_ps_clk;
wire FIXED_IO_ps_porb;
wire FIXED_IO_ps_srstb;
ps_downloadtest ps_downloadtest_i
(.DDR_addr(DDR_addr),
.DDR_ba(DDR_ba),
.DDR_cas_n(DDR_cas_n),
.DDR_ck_n(DDR_ck_n),
.DDR_ck_p(DDR_ck_p),
.DDR_cke(DDR_cke),
.DDR_cs_n(DDR_cs_n),
.DDR_dm(DDR_dm),
.DDR_dq(DDR_dq),
.DDR_dqs_n(DDR_dqs_n),
.DDR_dqs_p(DDR_dqs_p),
.DDR_odt(DDR_odt),
.DDR_ras_n(DDR_ras_n),
.DDR_reset_n(DDR_reset_n),
.DDR_we_n(DDR_we_n),
.FIXED_IO_ddr_vrn(FIXED_IO_ddr_vrn),
.FIXED_IO_ddr_vrp(FIXED_IO_ddr_vrp),
.FIXED_IO_mio(FIXED_IO_mio),
.FIXED_IO_ps_clk(FIXED_IO_ps_clk),
.FIXED_IO_ps_porb(FIXED_IO_ps_porb),
.FIXED_IO_ps_srstb(FIXED_IO_ps_srstb));
///自行添加的PL例化模块
GyroControlSystem u_GyroControlSystem(
.sys_clk ( sys_clk ),
.sys_rst_n ( sys_rst_n ),
.led ( led ),
.lcd_hs ( lcd_hs ),
.lcd_vs ( lcd_vs ),
.lcd_de ( lcd_de ),
.lcd_rgb ( lcd_rgb ),
.lcd_bl ( lcd_bl ),
.lcd_clk ( lcd_clk ),
.uart_rxd ( uart_rxd ),
.uart_txd ( uart_txd ),
.dds_out ( dds_out )
);
endmodule
2.10 例化PL顶层模块进wrapper后,整个工程就wrapper一个顶层文件,其下面包含zynq的bd部分和原PL部分。
2.11 以wrapper为顶层文件(‘品’字符前面就是顶层文件),重新生成bitstream。
!!! 注意,此时综合布局后的PL资源占用,应该与前面单独PL综合后差不多,最多差个几个十几个lut,如果相差太多如从5000降到200,那就是前面哪里出了问题。很可能就是生成wrapper好几次,导致工程混乱,这种情况最直接的办法就是重新建工程重来一遍。
2.12 重新生成bitstream后,导出Hardware文件,特别注意要勾选Include bitstream。
2.13 此时会默认在工程文件夹下生成.xsa的文件,这就是后面创建vitis工程需要的hardware文件。
3. 创建vitis工程,并生成bin文件。
3.1 打开vitis2019.2,新建工程,注意名称和路径,vitis的路径应该和vivado是独立的,然后next
3.2 添加第2节生成的xsa硬件模板文件,可以把刚才的xsa单独复制到特定的文件夹下管理,也可以直接在原目录下。
3.3 选中添加的硬件包,next
3.4 选中下面的生成 boot 组件配置
3.5 选择FSBL工程模板,等待生成完成。这个FSBL就是专用于固化下载的。
3.6 生成完FSBL工程后如下,左侧文件栏为两部分,硬件包的warpper.xsa解压出来的和fsbl工程。
3.7 编译工程:在fsbl_system项目上右键,选中build,等待编译完成。
编译完成,且没有报错信息
3.8 先直接下载程序至FPGA,看是否正常运行,注意此时程序还是掉电即消失。
另外注意,直至此时启动拨码开关都是JTAG模式。
3.9 下载正常,即可生成所需要的bin文件。
3.10 在工程上选择,软件会自动添加相应路径,直接生成就可无需其他更改,记住生成的路径:
4. 加载bin文件到SD卡或flash中
4.1 如果是通过sd卡启动,则直接复制刚才的bin文件到sd卡的一级目录中去就行,然后板子选择启动方式为SD,插入sd重新上电即完成。如果不行,可以尝试格式化一下sd卡再尝试。
4.2 如果格式化到qspi flash芯片则进行如下两种操作,有什么目的上的区别嘛,我目前不知道,只知道操作不一样:
首先:vitis工程创建完成后,目录下会有3个工程文件夹分别是:带有wrapper的、带有_system结尾的文件夹、原来的工程名。前两个文件夹中都有elf文件,而bin文件只在最后一个文件夹中。
4.2.1 下载system工程至flash,此时1选择system, 2选择system文件夹中的bin文件,3选择wrapper文件夹下的elf文件。
4.2.2 下载application 至flash, 1选Application , 2选_system目录下的bin文件, 3选原名路径(不带后缀,非wrapper 和system)下的elf文件,等待flash操作完成,断电、重新选择启动拨码开关至qspi、上电。
5.注意事项
1. 如果显示spi speed fallback to 100khz, 可能是zynq核中忘记或者错误勾选qspi模块。
2. 如果显示flash下载成功,而且通过 run as 下载也正常,但是最后flash程序异常,很可能是vivado生成wrapper文件出现问题。
-----------2021.11.08