Vivado × Vitis 2019.2 固化 ZYNQ7000 系列 PL 端程序详细教程
认清形势,放弃幻想。
对于一般的 FPGA 来说,编写完 PL 端代码,直接烧录进 FLASH 或固化至 SD 卡相对较容易;但对于 ZYNQ7000 系列来说,却并没有那么简单。
ZYNQ7000 系列的启动流程由 PS 端主导,故当我们固化 PL 时,也必须引入
ARM 核控制的部分。如果你想问有没有不需要 PS
的办法,请看看文章开头的那句话。(为此,我还重装了一次
Vivado)
概括地说,固化程序的流程如下:
flowchart LR
A["建立PL工程"]
B["配置ARM核IP"]
C["生成比特流\nand\n导出硬件"]
D["建立Vitis工程"]
E["生成启动文件"]
F["烧录至\nFLASH\n(或SD)"]
A-->B-->C-->D-->E-->F
环境
- 板卡:ALINX ZYNQ 7010 开发板
- 软件:Vivado & Vitis 2019.2
- 系统:Windows 10 22H2
注意事项
- 本文教程仅适用于 2019.2 以上版本(“SDK” 已被 "Vitis" 取代,需要注意二者间的区别)。
- 本文教程适用于“仅有 PL 程序”时的固化,若含有 PS 端,本文可能不适合(可以适当参照本文的“参考”部分)。
- 正文部分的一些 IO 配置可能需要依板卡型号的不同而作出相应调整。
- 在烧录过程中,请特别留意教程中有关选择“启动模式”的说明。
正文
以固化一个最简单的“呼吸灯”为例。
建立 PL 端项目
(这部分和平时建工程没什么区别。)
建立工程
打开 Vivado,点击 File
- Project
-
New
。
选择 RTL 类型。
创建 RTL 源码文件。
约束先跳过,不建立文件,进行器件选型。
暂时不定义端口。
编辑源码
编辑 led.v
写入代码:
1 | module led(input sys_clk, |
请留意这个模块所定义的输入输出端口,之后会用到。
操作如图所示,保存后,进行 RTL 分析。
修改管脚约束
如图,打开 I/O Ports
。
修改管脚约束如下。
保存文件并命名。
添加时序约束
如图,点击 Run Synthesis
进行综合,需要等待一段时间后综合才会完成。
综合完成后,对话框点 Cancel
。
随后打开时序约束助手:
修改时钟为 50Mhz(板载晶振),Skip to Finish
-
Finish
。
生成 Bit 文件并进行验证
按图示生成比特流。
随后,把 Bit 文件下发到 FPGA 上进行测试,以确保功能正常。(不详述了)。
请确保此步骤的 BitStream 验证通过后再进行下一步操作。
配置 ARM 核 IP
上文的比特流验证成功后,说明我们想要的 PL 端程序已经大体没什么问题了。接下来就是引入 PS 端的驱动,好消息是,Xilinx 已经提供好 IP 了。
添加 IP 并配置
新建一个 Block Design
并命名。
在右侧添加图示的 IP 核。
双击添加后的图块进行配置,首先是 IO 口。
对于高速的 Flash(QSPI 时钟大于40MHz),需要展开
Quad SPI Flash
勾选 Feedback Clock
。
接下来配置 DDR,注意根据板子的手册选择兼容的内存型号。
(例如,hellofpga 的 mini ZYNQ 7000 v1.1 选型为MT41K128M16 JT125,数据位宽选择16bit)
调整 SPI 速率为 fast(可跳过)。
设置 Bank1 电压为 1.8V(视板卡情况而定——黑金的为1.8V,minifpga 的是 3.3V)。SD 卡部分如下。
配置完点 OK
即可。
随后按照图示连线把两个端口接起来,然后
Run Block Automation
,如果弹出对话框,直接点
OK
。
点击图示按钮初步检查 IP ,无误后继续。
注:经过实测,由于我的板子的 PL 端有独立的晶振,且我暂时用不到 PS
的功能。故 ip 核上只需要保留 DDR
、FIXED_IO
这两大端口即可,其余的都可以取消。操作流程如下:
PS-PL Configuration
-General
-Enable Clock Resets
取消勾选FCLK_RESET0_N
PS-PL Configuration
-AXI Non Secure Enablement
-GP Master AXI Interface
取消勾选M AXI GP0 interface
Clock Configuration
-PL Fabric Clock
取消勾选FCLK_CLK0
生成 wrapper
回到项目管理器,如图,在添加的 IP 上右键,选择
Generate Output Products
。
点击 Generate
。
观察项目结构,由下图可见,操作后生成了 ps_download
的
RTL 代码。
随后,继续按照下图操作,生成 wrapper。
此时的项目结构如下图,wrapper 和 led 模块都位于顶层。
修改 wrapper
接下来需要修改 wrapper,将其变为顶层,而 led 则作为其子模块被例化。
我们的工作有:
为 wrapper 这个 module 添加原先 led 模块所具有的端口
在 wrapper 中例化 led
修改后的 wrapper 代码如下,需要重点关注的是我们自行添加的部分,这些在注释中做了说明。
1 | //Copyright 1986-2019 Xilinx, Inc. All Rights Reserved. |
可以发现,代码中的 sys_clk
、rst_n
和
led
就是上文中 PL 工程中 led module 的输入和输出端口。
修改后,项目最终结构如下图,只有一个 wrapper 作为顶层文件,其下包含了 PS 端的驱动以及我们的 led 模块:
注意,此时一定要确保 wrapper 文件为顶层,可以在其上 右键 -
Set as Top
以确保万无一失。
笔者在使用 Vivado 2022.2 时,出现了 wrapper 不为顶层的情况,最终 VITIS 生成的固件只能在线运行,而断电后无法运行。
生成比特流 & 导出硬件
生成比特流
留意一下当前板子上资源的占用情况,然后重新生成比特流。
注意,重新生成后,资源占用情况应该和原来的差不多,否则可能有问题。
生成完毕后将比特流下载到 FPGA,验证是否正常工作。
还是那句话,请确保此步骤的 BitStream 验证通过后再进行下一步操作。
导出硬件包
验证成功后,按图示操作导出硬件包,注意勾选
Include Bitstream
。
如果报错,如图:
可以试着 Run Implementation
然后
Open Implemented Design
,随后再执行导出操作。
导出可能较慢,需要耐心等待。
建立 PS 端项目
启动 Vitis,选取一个空目录作为 WorkSpace。然后点击 File
- New
-Application Project
。
为项目命名并选择路径,注意,新建的项目的路径应当和上述 Vivado 工程的路径区分开。
添加刚才在 Vivado 导出的 xsa 硬件文件。
导入后如图所示:
继续进行生成 boot 组件的配置,注意勾选
Generate boot components
。
选取 Zynq FSBL
作为模板(专用于固化下载),点击
Finish
后等待生成完成。
生成完毕后,左侧文件栏中可见有解压后的硬件包(绿色标)和 fsbl 工程(蓝色标)。
随后,在 fsbl 工程上右键 - Build Project
。
Tips:如果对下方绿色的硬件包右键,可以看到
Update Hardware Specification
选项,它可用于在硬件包发生变化后,进行硬件包的更新。(更新后可能还需要执行一下项目的Clean
和Build
操作)
确认编译完成没有报错后,可将程序临时下载至 FPGA 进行测试,此时的程序还是掉电即消失。
将 FPGA 的启动模式设置为 JTAG 后,上电,按图示步骤操作:
下载完成后,验证程序是否正常运行。
请确保此步骤验证通过后再进行下一步操作。
生成启动文件
确认无误后,可以按图示操作生成启动文件。
生成路径一般无需修改,我们只需要记住就行。
固化程序
“万事俱备,只欠东风。”
有了启动文件,就可以进行程序固化操作了。
有以下两种方式可供选择。
调整启动模式为 JTAG 模式后,重新上电并连接电脑。
在 fsbl 工程上右键,选择
Program Flash
。在弹出的窗口中进行如下设置,随后点击
Program
进行下载:暂时没搞明白选“System”和“Application”有什么区别
调整启动模式为QSPI 模式,重新上电,验证运行情况。
注意:
-
如果显示
spi speed fallback to 100khz
,可能是启动模式未设置为 JTAG,或者是 ZYNQ 核中忘记或者错误勾选 QSPI 模块。 -
如果显示 flash 下载成功,而且通过
run as
下载也正常,但是最后运行异常,很可能是 vivado 生成 wrapper 文件出现问题。
以下内容待测试
复制生成的 BOOT.bin 至 SD 卡根目录。
调整启动模式为 SD 。
插入 SD 卡并上电。
参考
详细教程:vivado2019.2 & vitis2019.2下,zynq7000系列FPGA固化PL程序到外挂flash和SD卡
Zynq> ERROR: [Xicom 50-186] Error while detecting SPI flash device - unrecognized JEDEC id bytes: 00, 00, 00