blog_vitis_platform_system_application

从 XSA 到 Hello World:Vitis 里的 Platform、System、Application 解析vitis工程树

这篇文章记录我第一次把 Vivado 导出的 Zynq 硬件系统接到 Vitis 软件工程时,遇到的几个最容易混淆的概念:PlatformSystemApplication,以及 application 下面 src/ 里那几个文件到底分别负责什么。

我的环境大致是:

text 复制代码
Board: Mizar Z7 / ZYNQ-7020
Tool : Vivado/Vitis 2020.2
Flow : Vivado block design -> export XSA -> Vitis standalone application
XSA  : accel_axi_112.xsa

这一步还不是验证 FPGA GEMM IP 的功能,而是先把最基础的软件链路跑通:

text 复制代码
Vivado XSA
  -> Vitis Platform
  -> standalone Application
  -> ELF 下载到 ARM Cortex-A9
  -> UART 打印
  -> PS 读写 DDR

如果这条链路不通,后面再去调 AXI-Lite、HP DDR、HLS IP 和 GEMM 结果,很容易把问题混在一起。

先看一张关系图

我现在更愿意这样理解 Vitis 工程:

text 复制代码
XSA
  Vivado 导出的硬件说明包
  里面包含 PS 配置、DDR、UART、AXI IP 地址、bitstream 等信息

Platform Project
  Vitis 根据 XSA 生成的软件平台
  里面有 BSP、驱动、xparameters.h、system.mss、FSBL 等

Domain
  application 运行的软件环境
  例如 standalone on ps7_cortexa9_0

Application Project
  我真正写 main() 的地方
  例如 Hello World、DDR test、AXI-Lite test、GEMM test

System Project
  把 application、domain、platform 组织在一起的外层工程

放到我这个工程里,大概对应:

text 复制代码
accel_axi_112_uart                  Platform Project
accel_axi_112_hello                 Application Project
accel_axi_112_hello_system          System Project

accel_axi_112_ddr_test              Application Project
accel_axi_112_ddr_test_system       System Project

这里最重要的一点是:Application 不是孤立运行的,它依赖某个 Platform。如果 Vivado 硬件改了,比如 UART、DDR、AXI 地址或 HLS IP 变了,就应该重新导出 XSA,并更新或重建 platform。

XSA 是什么

XSA 可以理解成 Vivado 交给 Vitis 的硬件说明文件。Vivado 负责搭硬件系统、生成 bitstream,Vitis 负责在这个硬件系统上写 ARM 端软件。

XSA 里会告诉 Vitis:

text 复制代码
PS7 怎么初始化
DDR 地址范围和初始化参数是什么
UART 走哪个 MIO
AXI-Lite 外设基地址是什么
HLS IP 有哪些寄存器和驱动
bitstream 在哪里

我这里使用的 XSA 是:

text 复制代码
gzy_gemm_accel/vivado_board/accel_axi_112/export/accel_axi_112.xsa

如果 XSA 里没有 UART,Vitis 创建 Hello World application 时可能会提示:

text 复制代码
This application requires a Uart IP in the hardware.

这个问题不能靠改 helloworld.c 解决。它说明当前 platform 对应的硬件说明里没有可用 UART,需要回 Vivado 配好 UART,重新导出 XSA,再重建或更新 platform。

Platform Project 是什么

Platform Project 是 Vitis 根据 XSA 生成的软件平台。它不是业务程序,而是"这块板子的软件基础设施"。

它通常会生成:

text 复制代码
BSP
  Board Support Package,包含 standalone runtime、startup、Xilinx 驱动和库

xparameters.h
  根据 XSA 自动生成的硬件宏,例如 UART、DDR、AXI IP 的基地址

system.mss
  BSP 配置,例如 stdin/stdout 使用哪个 UART

FSBL
  First Stage Boot Loader,后续做 BOOT.BIN 或固化启动时会用到

所以我把 platform 理解成:

text 复制代码
Vitis 眼里的这块板子。

在 Project Explorer 里,accel_axi_112_uart 就是我的 platform。这个名字里的 uart 只是我为了区分旧 platform 起的名字,因为之前有一版 XSA 没有配好 UART。

Application Project 是什么

Application Project 才是我写 main() 的地方。

例如:

text 复制代码
accel_axi_112_hello
  最基础的 Hello World,验证 JTAG 下载、ARM 运行、UART 打印

accel_axi_112_ddr_test
  DDR sanity test,验证 PS 程序跑在 DDR,并且 PS 可以读写 DDR

后续 GEMM application
  PS 准备 A/B/instr,配置 accelerator_top_axi,启动 PL,读回 C

第一次做 sanity test 时,我建议 template 选 Hello World,不要一开始选 Empty Application。因为 Hello World 模板会自动带好串口打印和平台初始化文件,适合一步一步排查。

创建 application 时关键选项是:

text 复制代码
Platform:
  accel_axi_112_uart

Processor:
  ps7_cortexa9_0

Domain:
  standalone on ps7_cortexa9_0

Template:
  Hello World

standalone 的意思是不跑 Linux,直接裸机运行在 ARM Cortex-A9 上。第一次上板验证时,standalone 最直接,变量也最少。

System Project 是什么

System Project 是 Vitis 组织 application 的外层壳。它会把 application、domain、platform 关联起来。

比如:

text 复制代码
accel_axi_112_ddr_test_system
  -> accel_axi_112_ddr_test
     -> standalone on ps7_cortexa9_0
     -> based on accel_axi_112_uart platform

日常写代码时,我主要关注 application;需要运行、调试或查看组织关系时,才会注意 system。

一个容易混的点是:原来的 accel_axi_112_hello_system 不会自动影响新的 accel_axi_112_ddr_test_system。它们是不同的 system。真正要小心的是右键 Build 或 Run As 的时候不要点错 application,否则可能运行了旧的 Hello World。

Vitis 界面里常看的窗口

Vitis 2020.2 基于 Eclipse,界面一开始会有点绕。我目前最常用的是这几个区域。

Project Explorer

左侧工程树。这里能看到 platform、application、system。

如果看不到,可以打开:

text 复制代码
Window -> Show View -> Project Explorer

我通常在这里做:

text 复制代码
右键 platform -> Build Project
右键 application -> Build Project
右键 application -> Run As -> Launch on Hardware
打开 application/src/helloworld.c
打开 application/src/lscript.ld

Editor

中间编辑区。打开 helloworld.c 时是普通 C 代码编辑器;打开 lscript.ld 时,Vitis 可能显示图形化的 Linker Script 页面。

lscript.ld 通常有两个页签:

text 复制代码
Summary
  图形界面,能看到 Memory 区域和 Section to Memory Region Mapping

Source
  真实 linker script 文本

如果只是检查程序放在 DDR 还是 OCM,Summary 页已经够用了。要批量修改或确认细节时,看 Source 更直接。

Console

普通编译和运行日志窗口。Build、Launch on Hardware、下载 ELF 的信息经常在这里出现。

Build 成功时,一般能看到类似:

text 复制代码
Build Finished

Application build 成功后,会生成:

text 复制代码
Debug/<application_name>.elf

ELF 就是 ARM 端可执行文件。

Problems

编译错误和警告列表。如果 build 报错,我会先看 Console 的完整日志,再看 Problems 定位是哪一行。

Vitis Log

IDE 自己的操作日志。它更偏工具内部行为,不是每次都需要看。

XSCT Console

XSCT 是 Xilinx Software Command-line Tool。它可以用 Tcl 命令连接 JTAG、选择 ARM target、读写内存、下载 ELF。

如果看到提示符:

text 复制代码
xsct%

说明它在等待输入命令。

常见命令有:

tcl 复制代码
connect
targets
targets -set -nocase -filter {name =~ "*A9*#0"}
stop
mwr 0x0 0x12345678
mrd 0x0

它们分别表示连接 hw_server、列出 JTAG/debug target、选择 ARM Cortex-A9 核 0、暂停 CPU、写内存、读内存。

targets 能看到:

text 复制代码
APU
ARM Cortex-A9 MPCore #0
ARM Cortex-A9 MPCore #1
xc7z020

说明 PS 侧调试链路和 PL 侧 JTAG 至少都能被工具看到。

src 目录里的文件分别干什么

accel_axi_112_ddr_test/src 为例,里面有:

text 复制代码
helloworld.c
platform_config.h
platform.c
platform.h
lscript.ld
Xilinx.spec

helloworld.c

这是 application 的主程序,里面有 main()

后续我写 DDR test、AXI-Lite register test、GEMM application,主要改的就是这个文件。

Hello World 模板最开始大概是:

c 复制代码
int main()
{
    init_platform();

    print("Hello World\n\r");
    print("Successfully ran Hello World application");

    cleanup_platform();
    return 0;
}

后面做 DDR sanity test 时,可以把它改成:

c 复制代码
#include "platform.h"
#include "xil_printf.h"
#include "xil_types.h"
#include "xil_cache.h"

#define DDR_TEST_ADDR 0x01000000U

int main()
{
    volatile u32 *ddr = (volatile u32 *)DDR_TEST_ADDR;
    u32 v0;
    u32 v1;

    init_platform();

    xil_printf("DDR sanity test start\r\n");
    xil_printf("DDR test addr = 0x%08x\r\n", DDR_TEST_ADDR);

    ddr[0] = 0x12345678U;
    ddr[1] = 0xDEADBEEFU;

    Xil_DCacheFlushRange((UINTPTR)ddr, 2 * sizeof(u32));
    Xil_DCacheInvalidateRange((UINTPTR)ddr, 2 * sizeof(u32));

    v0 = ddr[0];
    v1 = ddr[1];

    xil_printf("DDR[0] = 0x%08x\r\n", v0);
    xil_printf("DDR[1] = 0x%08x\r\n", v1);

    if (v0 == 0x12345678U && v1 == 0xDEADBEEFU) {
        xil_printf("DDR sanity PASS\r\n");
    } else {
        xil_printf("DDR sanity FAIL\r\n");
    }

    cleanup_platform();
    return 0;
}

这里的目标很小:PS 往 DDR 地址 0x01000000 写两个数,再读回来。如果串口打印的值一致,就说明:

text 复制代码
PS -> DDR

这层是通的。

platform.c

这是模板提供的平台初始化代码。里面主要有:

c 复制代码
init_platform();
cleanup_platform();
enable_caches();
disable_caches();
init_uart();

对 Zynq PS UART 来说,注释里也写得很清楚:PS7 UART 通常由 BootROM/BSP 配成 115200 bps。

初学阶段我不建议随便改 platform.c。一般只在 helloworld.c 里调用:

c 复制代码
init_platform();
cleanup_platform();

platform.h

platform.c 的头文件,声明:

c 复制代码
void init_platform();
void cleanup_platform();

业务代码里 #include "platform.h" 后,就能调用这两个函数。

platform_config.h

平台配置宏。比如我这里有:

c 复制代码
#define STDOUT_IS_PS7_UART
#define UART_DEVICE_ID 0

它告诉模板代码 stdout 使用 PS7 UART。一般不用手动改。

lscript.ld

这是 linker script,链接脚本。它决定程序的各个段放到哪个内存区域。

这个文件非常关键,因为它决定:

text 复制代码
.text   代码段放哪里
.rodata 只读数据放哪里
.data   已初始化全局变量放哪里
.bss    未初始化全局变量放哪里
.heap   malloc/new 使用的堆放哪里
.stack  函数调用栈放哪里

Zynq 工程里常见 memory 区域有:

ld 复制代码
MEMORY
{
   ps7_ddr_0 : ORIGIN = 0x100000, LENGTH = 0x3FF00000
   ps7_ram_0 : ORIGIN = 0x0, LENGTH = 0x30000
   ps7_ram_1 : ORIGIN = 0xFFFF0000, LENGTH = 0xFE00
}

其中:

text 复制代码
ps7_ddr_0
  外部 DDR,容量大,后续 GEMM 的 A/B/C buffer 主要会在这里。

ps7_ram_0
  PS OCM,On-Chip Memory,片上 RAM,地址 0x0 附近,容量小。

ps7_ram_1
  另一段片上 RAM 区域,地址接近 0xFFFF0000。

如果 lscript.ld 里看到:

ld 复制代码
} > ps7_ddr_0

说明对应 section 放在 DDR。

如果看到:

ld 复制代码
} > ps7_ram_0

说明对应 section 放在 OCM。

所以判断程序到底跑在哪里,不要靠猜,直接看 lscript.ld 或图形界面的 Section to Memory Region Mapping

Xilinx.spec

这是 Xilinx 工具链使用的 spec 文件,影响底层编译/链接启动文件选择。Hello World 模板里通常很小,例如:

text 复制代码
*startfile:
crti%O%s crtbegin%O%s

初学阶段不用碰它。

程序默认放 DDR 吗

在 Zynq standalone application 里,如果 XSA 里 DDR 配置正常,Vitis 生成 application 时通常会默认把主要 section 放到 ps7_ddr_0

但是这不是一条永远成立的规则。最终以 linker script 为准:

text 复制代码
.text/.data/.bss/.heap/.stack -> ps7_ddr_0
  程序主要跑在 DDR

.text/.data/.bss/.heap/.stack -> ps7_ram_0
  程序主要跑在 OCM

有时候为了排查 DDR 是否有问题,可以故意把 Hello World 放到 OCM。这样能把问题拆开:

text 复制代码
Hello World 放 OCM 也跑不起来:
  先查 JTAG、PS debug、boot mode、reset。

Hello World 放 OCM 能跑,但放 DDR 不行:
  再查 DDR 初始化、linker script、ps7_init、硬件 DDR 配置。

我之前能在 OCM 附近跑 Hello World,说明 JTAG 下载、ARM 运行和 UART 输出基本通了。下一步再把程序放回 DDR,并加 DDR 读写测试,就是为了验证 PS -> DDR 这一层。

Build、Clean、Launch 分别是什么

Build Project

Build Project 是编译当前工程。

对 platform 来说,它会编译 BSP、驱动和库。

对 application 来说,它会把:

text 复制代码
helloworld.c
platform.c
startup code
BSP library
lscript.ld

编译链接成:

text 复制代码
Debug/<application_name>.elf

如果只是改了 helloworld.c,通常直接 Build 就够了。

Clean Project

Clean Project 是清掉旧的编译产物,例如:

text 复制代码
Debug/*.o
Debug/*.elf

它不是删除源码,也不会删除 application。

什么时候建议 Clean?

text 复制代码
刚改了 helloworld.c:
  可以直接 Build

刚改了 lscript.ld:
  建议 Clean 后再 Build

刚更新了 platform / BSP / XSA:
  建议 Clean 后再 Build

Clean 主要是防止 Vitis/Eclipse 没有正确发现生成配置或 linker script 的变化。

Run As -> Launch on Hardware

右键 application:

text 复制代码
Run As -> Launch on Hardware

它不是简单运行一个 Windows exe,而是通过 JTAG 把程序放到板上的 ARM 里运行。

通常会做这些事:

text 复制代码
连接 hw_server
通过 JTAG 连接板子
下载 bitstream 到 PL
运行 ps7_init.tcl 初始化 PS
下载 application ELF 到 ARM Cortex-A9
设置 PC 到程序入口
启动 ARM 运行

Launch 成功时,Console 里常见类似:

text 复制代码
Downloading Program -- .../Debug/accel_axi_112_ddr_test.elf
Setting PC to Program Start Address ...
Successfully downloaded ...
Info: ARM Cortex-A9 MPCore #0 Running

同时串口工具应该能看到程序打印。

小结

我现在对 Vitis 这几个概念的理解是:

text 复制代码
XSA:
  Vivado 导出的硬件交接文件。

Platform:
  Vitis 根据 XSA 生成的板级软件基础。

Domain:
  application 运行的软件环境,比如 standalone on ps7_cortexa9_0。

Application:
  我写 main() 的地方,最终生成 ELF。

System:
  把 application、domain、platform 组织起来的外层工程。

lscript.ld:
  决定程序段放 DDR 还是 OCM 的关键文件。

第一次上板不要急着验证完整算法。先让 Hello World 通,再让 DDR sanity test 通,再测 AXI-Lite 寄存器,最后再跑 PS-PL-DDR 的 GEMM application。这样每一步失败时,都能知道自己正在排查哪一层。

相关推荐
zlinear数据采集卡2 小时前
单点接地设计电路深度解析:从理论原理到ZLinear采集卡的低噪声实战
c语言·单片机·嵌入式硬件·fpga开发
湉湉家的小虎子3 小时前
【科普贴】浅谈UFS接口——偏硬件解析
驱动开发·嵌入式硬件·fpga开发·硬件工程
2301_809049424 小时前
phase3_note_vivado_2020_ip_packager_revision
linux·fpga开发
GateWorld5 小时前
LCD显示技术完全指南:原理·制造·驱动·FPGA实现之点屏二
fpga开发·lcd显示·fpga点亮屏幕·minilvds
汽车仪器仪表相关领域1 天前
南华 NHA-604/605 汽车排放气体测试仪:国六b全适配高精度便携检测设备
大数据·人工智能·功能测试·深度学习·安全·fpga开发·压力测试
一口一口吃成大V1 天前
使用PLL的lock信号作为复位信号
fpga开发
hexiaoyan8271 天前
图像分析与测试卡学习资料第216篇:基于FMC接口的1路full Camera Link输入 1路HDMI(DVI)输出子卡
fpga开发·图像分析与测试·数字成像
zlinear数据采集卡2 天前
电源纹波杀手:LDO线性稳压电路的“降噪哲学”——基于ZLinear数据采集卡的深度解析
单片机·嵌入式硬件·fpga开发·硬件架构