1. 为什么还要从 hello_world 开始聊 Vitis 2024.1
如果你最近刚把开发环境升级到 Vitis 2024.1,打开界面之后大概率会愣一下——菜单结构变了,工程类型变了,连新建平台的入口都藏得比以前深。我身边好几个做嵌入式开发的朋友都在群里吐槽,说以前在 SDK 里点几下就能跑通的 hello_world,换到 Vitis 之后反而不知道从哪儿下手了。这不是你一个人的问题,而是整个工具链从 Xilinx 时代过渡到 AMD 统一软件栈之后,工作流发生了一次比较大的重构。
Vitis 2024.1 是 AMD 把硬件平台、嵌入式软件开发、加速应用开发整合到一起之后的版本,它同时支持 Zynq、Zynq UltraScale+、MicroBlaze 以及 Versal 等多个器件系列。对于刚接触这套工具的人来说,hello_world 依然是最好的切入点——它足够简单,但又能把“平台工程 → 应用工程 → 编译 → 下载 → 串口输出”这条完整链路跑通。你把这套流程走顺了,后面做裸机驱动、FreeRTOS 移植、甚至 AI 推理加速,底层逻辑都是一样的。
这篇文章面向的是刚上手 Vitis 2024.1 的嵌入式开发者,不管你是学生、转岗工程师,还是从老版本 SDK 迁移过来的老手,我都会把每一步的操作意图、参数含义和容易踩的坑讲清楚。我不会只告诉你“点这个按钮”,而是会解释为什么要点它、不点会怎样、点错了怎么退回来。读完你至少能做到三件事:独立创建一个能跑 hello_world 的 Vitis 工程、理解 platform 和 application 两个工程之间的关系、知道串口没输出时该从哪几个方向排查。
2. Vitis 2024.1 的工作流到底变了什么
2.1 从 SDK 到 Vitis 的核心思路转变
老版本 SDK 的逻辑是“先有硬件 handoff,再建应用”,平台信息基本是隐式带过来的。Vitis 把这件事显式化了:你必须先有一个Platform Project(平台工程),它描述了目标器件的硬件信息、处理器核、内存映射、外设地址等;然后才能基于这个平台创建Application Project(应用工程)。这个设计一开始会让人觉得多了一步,但好处很明显——同一个平台可以挂多个应用工程,团队协作时硬件描述和应用代码可以分开维护。
打个比方,Platform Project 就像是一张“地图”,标明了这块板子上有哪些资源、内存从哪到哪、串口挂在哪个地址;Application Project 则是“在这张地图上跑的一辆车”。没有地图,车不知道往哪开;地图画错了,车就会跑飞。所以你在 Vitis 里遇到的绝大多数“编译过了但跑不起来”的问题,根源都在平台工程这一层。
2.2 2024.1 版本几个值得注意的变化
Vitis 2024.1 相比前几个版本,有几个地方需要特别留意。第一,统一安装器把 Vitis、Vivado、Vitis HLS 打包在一起,安装体积比以前大不少,建议预留至少 200GB 磁盘空间,而且安装时一定要勾选对应器件的 device family,不然后面建平台时会发现找不到器件。第二,Workspace 的概念被强化,一个 workspace 下可以放多个 platform 和 application,切换时不用重启,但 workspace 路径里千万不要有中文和空格,这是老生常谈但每年都有人栽跟头的地方。第三,串口终端在 2024.1 里集成得更好了,但如果你用的是 USB 转串口模块,驱动没装好的话 Vitis 自带的 Terminal 是认不到端口的。
还有一个容易被忽略的点:Vitis 2024.1 对Java 运行环境有要求,某些 Linux 发行版自带的 OpenJDK 版本过高会导致启动器报错,遇到这种情况需要手动指定兼容的 JDK。Windows 用户相对省心,但也要确保系统里的 Visual C++ 运行库是最新的,否则可能出现工程创建到一半卡死的情况。
2.3 建工程之前必须确认的三件事
在动手之前,我建议你先花五分钟确认三件事,能省掉后面至少半小时的排查时间。
- 硬件平台是否明确:你用的是哪块板子?是官方开发板(比如 ZCU102、PYNQ-Z2)还是自制的?官方板一般有现成的 board file,自制板需要自己准备 XSA 文件。
- XSA 文件是否就绪:XSA 是 Vivado 导出的硬件描述文件,Vitis 建平台时要用它。如果你手上只有比特流没有 XSA,那得回 Vivado 重新导出。
- 串口连接是否正常:板子上电后,设备管理器里能不能看到串口?波特率通常默认 115200,但有些板子是 9600,这个要提前查清楚。
提示:如果你用的是官方开发板,Vitis 2024.1 内置了 board file,建平台时可以直接选板子型号,不用自己导 XSA。但自制板必须走 XSA 这条路,没有捷径。
3. 手把手创建第一个 Platform Project
3.1 新建平台工程的完整步骤
打开 Vitis 2024.1,第一件事是选 workspace。我一般会在 D 盘或者用户目录下建一个纯英文路径,比如D:\vitis_ws\hello_demo。选好之后进入主界面,从菜单栏File → New → Platform Project开始。
第一步是给平台工程起名,比如hello_platform。这里有个细节:工程名不要用中文,也不要用特殊字符,虽然界面支持,但后面生成 BSP 时路径拼接容易出问题。点 Next 之后进入平台来源选择,如果你有 XSA 文件就选Hardware Specification,然后浏览到 XSA 路径;如果是官方板就选Board,从列表里挑对应型号。
接下来是操作系统和处理器选择。跑 hello_world 的话,Operating System 选standalone(裸机),Processor 选你板子上实际用的核,比如psu_cortexa53_0(Zynq UltraScale+)或者ps7_cortexa9_0(Zynq-7000)。这一步选错了后面编译能过但下载会失败,所以一定要对着原理图或者板子手册确认。
3.2 平台工程里的关键配置项
平台工程建好之后,双击platform.spr文件会打开平台配置界面。这里有几个地方值得展开说。
BSP 配置里可以调整标准输入输出设备。默认情况下,stdin 和 stdout 都指向psu_uart_0或者ps7_uart_0,也就是板子上那个调试串口。如果你发现 hello_world 跑起来但串口没输出,第一件事就是回来检查这里有没有指错。有些板子有两个串口,一个连 FTDI 芯片,一个引到排针,指错了自然看不到打印。
内存配置里要确认 heap 和 stack 的大小。hello_world 用不了多少内存,默认值就够,但如果你后面要加 printf 浮点格式化或者用 malloc,就得适当调大。我一般会把 heap 设成 0x2000 以上,stack 设成 0x4000,留足余量。
编译器优化等级默认是-O2,调试阶段建议改成-O0并加上-g,这样单步调试时变量不会被优化掉。等代码稳定了再切回-O2减小体积。
3.3 编译平台工程与常见报错处理
配置完之后右键平台工程选Build Project。这一步会生成 BSP、硬件描述和链接脚本,第一次编译通常要几分钟。常见的报错有这么几类:
| 报错信息 | 可能原因 | 解决办法 |
|---|---|---|
XSA file not found | XSA 路径含中文或已被移动 | 把 XSA 放到纯英文路径下重新指定 |
No processor found | 器件 family 没装 | 回安装器补装对应 device family |
BSP generation failed | workspace 路径有空格 | 换一个无空格的 workspace |
Java heap space | 默认内存不够 | 修改 vitis 启动脚本里的 JVM 参数 |
编译成功后,平台工程目录下会出现export文件夹,里面是后续应用工程要引用的内容。看到这个文件夹,说明平台这层已经稳了。
4. 创建 Application Project 并跑通 hello_world
4.1 新建应用工程与模板选择
平台编译通过之后,File → New → Application Project。第一步选平台,下拉列表里应该能看到刚才建的hello_platform。如果看不到,说明平台没编译成功或者 workspace 没刷新,右键平台工程点Refresh再试。
给应用工程起名,比如hello_app。下一步是选模板,Vitis 2024.1 内置了好几个模板,我们要的是Hello World。选中之后它会自动生成一个helloworld.c,里面就一句printf("Hello World\n\r")。别小看这一句,它背后牵扯到 BSP 里的 UART 驱动、标准库的 retarget 实现、链接脚本里的内存布局,能跑通说明整条链路都通了。
模板选完还有一步是选处理器核,这里要和平台工程里选的一致。都确认无误后点 Finish,Vitis 会自动生成工程结构并开始编译。
4.2 helloworld.c 里到底发生了什么
打开生成的helloworld.c,代码大概长这样:
#include <stdio.h> #include "platform.h" #include "xil_printf.h" int main() { init_platform(); printf("Hello World\n\r"); cleanup_platform(); return 0; }init_platform()和cleanup_platform()是 Vitis 模板自动加的,主要做缓存和中断控制器的初始化,裸机程序里可以留着也可以删掉。真正干活的是printf。在嵌入式裸机环境里,printf并不是直接往屏幕输出,而是通过 BSP 里的outbyte函数重定向到 UART。这个重定向过程在 BSP 的xil_printf.c或者print.c里实现,你可以理解为“把标准输出的水管接到了串口上”。
如果你把printf换成xil_printf,输出也能出来,但两者有区别:printf支持浮点和完整格式化,代码体积大;xil_printf精简,不支持浮点,但占用空间小。跑 hello_world 用哪个都行,实际项目里我一般调试阶段用printf,量产时换xil_printf省空间。
4.3 编译、下载与串口验证
代码不用改,直接右键应用工程选Build Project。编译成功后,把板子连上电脑,确认串口驱动装好,然后在 Vitis 里右键应用工程选Run As → Launch Hardware。Vitis 会自动完成下载比特流、下载 ELF、启动处理器这一系列动作。
下载完成后,打开 Vitis 自带的 Terminal(Window → Show View → Terminal),新建一个串口连接,选对 COM 口,波特率 115200,数据位 8,停止位 1,无校验。点连接之后按一下板子的复位键,你应该能看到Hello World打印出来。
如果没看到输出,先别急着改代码,按这个顺序排查:串口选对没有 → 波特率对不对 → 板子是不是真的在跑(看下载时有没有报错)→ BSP 里 stdout 指向的 UART 和实际接线是不是同一个。我遇到过好几次都是因为板子上有两个串口,Terminal 连错了那个。
5. 那些文档里不会写的踩坑经验
5.1 串口没输出的五种可能
跑 hello_world 最常遇到的问题就是“下载成功了但串口没反应”。我把这些年遇到的坑整理成一张速查表,你按顺序过一遍基本能定位。
| 现象 | 排查方向 | 具体操作 |
|---|---|---|
| 完全无输出 | 串口选错 | 换另一个 COM 口试,或用串口助手交叉验证 |
| 输出乱码 | 波特率不匹配 | 试 9600、115200、57600 三档 |
| 输出一次就停 | 程序跑飞 | 检查 stack 是否溢出,调大 stack 再试 |
| 下载报错 | 比特流不匹配 | 确认 XSA 和板子型号一致 |
| 时有时无 | 供电不稳 | 换 USB 口或外接电源 |
还有一个隐蔽的坑:某些 USB 转串口芯片在 Vitis Terminal 里表现不稳定,换成 Putty 或者 minicom 反而正常。所以当你在 Vitis 里死活看不到输出时,不妨换个串口工具验证一下,能快速排除是工具问题还是程序问题。
5.2 平台工程和应用工程的依赖关系
很多人建完平台工程之后,改了硬件配置却忘了重新编译平台,结果应用工程用的还是旧的 BSP,出现各种莫名其妙的链接错误。记住一条原则:只要平台层的配置变了(比如改了 UART、加了外设、调了内存),就必须重新编译平台工程,然后应用工程也要重新编译。Vitis 不会自动帮你做这件事,它只会提示你 BSP 过期,但很多人会忽略那个黄色警告。
另外,应用工程里不要手动去改 BSP 生成的代码,那些文件在重新编译平台时会被覆盖。要改配置就回平台工程的 BSP 设置里改,这是唯一正确的入口。
5.3 调试器连接失败怎么办
有时候下载和运行都正常,但一想单步调试就提示“无法连接调试器”。这种情况多半是JTAG 被占用或者调试配置选错了。先确认板子上没有其他程序在占用 JTAG,比如另一个 IDE 或者烧录工具。然后在 Vitis 的 Debug Configuration 里检查目标处理器选对没有,Zynq 和 ZynqMP 的调试配置不一样,选错了就连不上。
还有一个冷门原因:某些板子的 JTAG 时钟频率设太高会导致连接不稳定,在 Debug Configuration 里把 JTAG 频率从默认的 15MHz 降到 5MHz 往往能解决。这个技巧我在官方文档里没见过,是实际调试时试出来的。
6. 从 hello_world 往下还能做什么
hello_world 跑通之后,你其实已经掌握了 Vitis 最核心的工作流。接下来可以顺着这个工程往下扩展:把printf换成自定义的日志函数、加一个 GPIO 点灯、引入中断控制器做个按键响应、或者把裸机程序改成 FreeRTOS 版本。这些操作的工程结构和 hello_world 完全一样,只是 BSP 里要多勾几个驱动库。
我个人建议下一步先做GPIO 点灯,因为它能帮你理解 Vitis 里怎么访问外设寄存器、怎么用 BSP 提供的驱动 API。再往后可以试试定时器中断,这一步会涉及中断向量表和 ISR 注册,是裸机开发的分水岭。等你把这些都跑顺了,再回头看 hello_world,会发现它虽然简单,但把该铺的路都铺好了。
最后分享一个小习惯:每建一个新工程,我都会先把 hello_world 跑一遍再写业务代码。这花不了五分钟,但能确认工具链、板子、串口、下载器全都是好的。如果 hello_world 都跑不起来,那问题一定在环境而不在你的代码,排查方向会清晰很多。这个习惯帮我省下的时间,远比那五分钟多得多。