1. 为什么需要无DDR的QSPI Flash启动方案
在嵌入式开发中,ZYNQ系列芯片因其强大的处理能力和灵活的FPGA架构而广受欢迎。但很多开发者可能不知道,当你的设计不需要大容量内存时,完全可以不接DDR内存芯片,直接从QSPI Flash启动系统。这种方案我在多个工业控制项目中实际应用过,不仅能降低硬件成本,还能减少PCB布局复杂度。
传统方案中,Vitis工具链默认假设系统会连接DDR内存,自动生成的FSBL(First Stage Boot Loader)也会依赖DDR进行初始化。这就导致了一个尴尬的情况:当你为了简化设计而移除DDR芯片后,系统反而无法正常启动了。我去年就遇到过这样的案例,客户的项目只需要运行简单的控制逻辑,却因为自动生成的FSBL强制要求DDR而不得不增加不必要的硬件成本。
无DDR方案的核心价值在于精简系统设计。根据我的实测数据,在XC7Z010这类低端型号上,仅使用片上OCM(On-Chip Memory)就能满足很多轻量级应用的需求。比如工业传感器数据采集、简单的人机界面控制等场景,OCM的256KB空间完全够用。这时候如果硬要加DDR,不仅增加BOM成本,还会带来额外的功耗和PCB面积开销。
2. 硬件设计与Vivado配置要点
2.1 ZYNQ核的基础配置
在Vivado中创建Block Design时,第一件事就是正确配置ZYNQ处理器核。我建议先打开"Presets"菜单选择适合你芯片型号的预设,比如"Zynq Z-7 Evaluation Board"就是个不错的起点。关键是要在DDR Configuration选项卡中,明确取消所有DDR相关的选项。
这里有个容易踩的坑:有些工程师会保留DDR配置"以防万一",觉得反正硬件上不接就行。但实测发现,只要在Vivado中启用了DDR配置,生成的硬件描述文件(xsa)就会包含DDR信息,导致后续FSBL编译出问题。正确的做法是彻底禁用DDR控制器,就像我上个月给客户调试时做的那样:
- 在Block Design中双击ZYNQ核
- 进入PS-PL Configuration → DDR Configuration
- 将"Enable DDR"选项取消勾选
- 确认Memory Part下拉框显示为
2.2 内存地址空间规划
禁用DDR后,我们需要合理规划OCM的内存布局。ZYNQ7系列通常有两块OCM:
- OCM_0:128KB
- OCM_1:128KB
在Vivado的Address Editor选项卡中,建议将OCM_0分配给FSBL使用,OCM_1留给应用程序。这种分配方式在我参与的机器人控制器项目中表现稳定。具体操作时要注意地址对齐问题,建议保持默认的0x00000000和0xFFFF0000这两个基地址不变。
3. Vitis开发环境的关键修改
3.1 平台工程的特殊处理
生成xsa文件后,在Vitis中创建平台工程时需要特别注意几个地方。首先新建Platform时,一定要选择"Generate boot components"选项。这个选项控制着是否自动创建FSBL工程,我们后续的修改都要基于这个FSBL进行。
我遇到过一个典型问题:有些工程师喜欢直接使用预编译的FSBL,但在无DDR场景下这绝对行不通。必须手动修改FSBL源码,以下是具体步骤:
- 在Vitis资源管理器右键点击平台工程
- 选择"Generate Boot Components"
- 在弹出的对话框中选择"Create Boot Image"
- 确认FSBL工程已正确生成
3.2 链接脚本的深度改造
FSBL和应用程序的链接脚本修改是无DDR方案的核心难点。根据我的项目经验,需要分别处理两个工程的链接脚本:
FSBL链接脚本修改要点:
MEMORY { ram_0 : ORIGIN = 0x00000000, LENGTH = 0x20000 } SECTIONS { .text : { *(.text) } > ram_0 /* 其他段也全部指向ram_0 */ }应用程序链接脚本修改:
MEMORY { ram_1 : ORIGIN = 0xFFFF0000, LENGTH = 0x20000 }这里有个实用技巧:我习惯在Vitis中先让工具自动生成默认链接脚本,然后复制出来手动修改。修改完成后再替换回去,这样可以避免遗漏必要的标准段定义。在最近的一个智能家居网关项目中,这种方法的可靠性得到了充分验证。
4. FSBL源码的关键修改点
4.1 DDR相关代码处理
FSBL源码中有多处需要修改才能完全去除DDR依赖。首先打开fsbl_main.c文件,找到DDR初始化的代码块。在2023.1版本的Vitis中,这段代码通常位于main()函数的开头部分:
#if defined(XPAR_PS7_DDR_0_S_AXI_BASEADDR) /* 原始DDR初始化代码 */ #else /* 添加无DDR时的替代初始化 */ xil_printf("No DDR detected, skip initialization\r\n"); #endif我建议保留这个条件编译结构,而不是直接删除DDR代码。这样既满足了无DDR的需求,又保持了代码的可移植性。在给客户做方案升级时,这种写法可以方便后续需要添加DDR的情况。
4.2 QSPI Flash配置调整
在qspi_flash.c文件中,需要特别注意Flash容量设置。由于没有DDR,我们通常需要更大的QSPI Flash来存储程序。修改方法如下:
// 注释掉自动检测的代码 // Status = XQspiPsu_GetFlashSize(QspiPsuPtr, &Flash_Size); // 手动设置为128MB Flash_Size = 0x8000000;这个修改在远程升级场景下特别重要。去年我在一个气象站项目中就遇到过因为Flash容量设置不当导致固件更新失败的问题。手动指定容量后,系统稳定性明显提升。
5. 实战验证与调试技巧
5.1 生成完整启动镜像
所有修改完成后,在Vitis中生成启动镜像时需要特别注意组件顺序。右击工程选择"Create Boot Image",在弹出的对话框中确保组件顺序为:
- FSBL
- 应用程序
- 比特流(如果有FPGA部分)
我开发了一个自动化脚本来自动完成这个过程,分享给大家:
bootgen -image boot.bif -arch zynq -o BOOT.bin -w on对应的boot.bif文件内容示例:
the_ROM_image: { [bootloader]fsbl.elf application.elf }5.2 常见问题排查
在实际调试中,有几个常见错误需要特别注意:
启动卡在FSBL阶段:通常是链接脚本配置不当导致。建议用JTAG连接开发板,在FSBL的main()函数入口处设置断点,单步跟踪执行流程。
应用程序无法运行:检查应用程序的链接地址是否与FSBL的跳转地址一致。我常用的验证方法是比较生成的map文件中的入口地址。
QSPI Flash读写失败:先用Vivado Hardware Manager单独测试Flash读写功能,确保硬件连接正常。必要时可以降低QSPI时钟频率试试。
在最近的一个客户案例中,他们遇到了启动成功率低的问题。经过排查发现是QSPI时钟线过长导致信号质量差,通过调整PCB布局和降低时钟频率到50MHz后问题解决。
6. 性能优化与进阶技巧
6.1 内存使用优化
在仅使用OCM的情况下,内存资源非常宝贵。我总结了几条优化建议:
- 使用-ffunction-sections和-fdata-sections编译选项
- 在链接时添加--gc-sections参数去除未使用的段
- 对于全局变量,尽量使用const修饰符
- 考虑使用内存池管理动态内存
这些技巧在我开发的智能电表固件中,成功将内存占用降低了约30%。
6.2 启动速度优化
无DDR方案的一个优势是启动速度更快。通过以下方法可以进一步优化:
- 减小FSBL体积:移除不必要的驱动初始化
- 优化QSPI读取速度:调整时钟分频系数
- 使用XIP(Execute in Place)技术
实测数据显示,经过优化的系统冷启动时间可以控制在200ms以内,这对工业控制场景非常有利。