这一篇在干嘛?
你的 FPSoC 设计(比如 DR1 系列)在 TD 里综合、布局布线完之后,PS 端那些时钟、IO、DDR 的寄存器配置还只存在于 TD 工程里,嵌入式软件完全”不知道”。HPF(Hardware Platform File)就是把这份硬件信息打包递给 FD(嵌入式集成开发环境)的快递箱。本篇基于官方《HPF Tool User Guide》(SWUG_503,TD5.9.1 / 2024.7),讲清导出操作、包内文件分工和软件侧的正确用法。同系列目录见 HPF 工具首页。
soc_plat.tcl 与 FD 调试:Run Platform Init
soc_plat.html 与 driver:查配置、拿基地址
HPF 是什么,为什么需要它
FPSoC 应用开发是一条”硬件 + 软件”的流水线:硬件工程师在 TD 里搭好 PS IP 和 PL 逻辑,软件工程师在 FD 里写跑在 CPU 上的程序。问题来了——PS 端上电后并不是”开箱即用”的:PLL 要配、外设时钟要开、PS IO 要设复用功能、DDR 要按你板子上那颗颗粒的时序初始化。这些配置全部来自 TD 工程里 PS IP 的图形化配置界面,软件侧无从得知。
HPF 文件就是这条信息通道:它把硬件设计信息、硬件配置信息从 TD 传递到软件设计。

具体来说,HPF 里携带的信息分两大类:
- 硬件配置信息:主要是 PS 端的寄存器配置,包括 clock(PLL 配置、外设时钟配置)、psio(PS 侧 IO 复用)、ps-pl(PS 与 PL 之间的接口总线)、ddr、peripheral(外设)、debug,以及 PL IP 驱动等部分。
- 硬件设计信息:器件信息、比特流文件、pl2ps 码点信息等。
一个重要前提:该功能仅支持 FPSoC 系列芯片(如 DR1 系列)。原因很直白——其他 FPGA 器件里根本没有 PS IP,没有 PS 寄存器可配,自然也就没有 HPF 之说。如果你的竞赛设计只用 EG4S20 这类纯 FPGA 器件,这一套工具用不上;一旦切到 DR1 这类 FPSoC 做软硬协同设计,HPF 就是必经之路。
导出流程:从 TD 工程到 .hpf 文件
第一步:确保工程里有且只有一个 PS IP
HPF 的导出依赖工程中包含 CPU 资源的设计。这里有一条硬性规则:
常见坑:PS IP 数量不对,导出按钮就"罢工"
- 设计中不包含 PS IP 时,TD 顶部菜单的 “Export Hardware Platform File” 按钮是灰色的,点了没反应——先检查是不是忘了加 PS IP。
- 设计中包含两个或以上 PS IP 时,TD 会直接报错提醒。一个 SoC 只有一颗处理子系统, duplicated 的 PS IP 没有意义,TD 也不允许。
第二步:进入导出页面
操作路径在 TD 顶部菜单栏:
- 点击 “Project” 菜单。
- 在弹出的选项卡中点击 “Export Hardware Platform File”,进入导出功能页面。

第三步:在导出页面完成三件事
导出页面本身非常简单,只有三个选项:

- 选择是否将比特流文件包含在 HPF 文件中(这个选择有讲究,见下)。
- 选择保存 HPF 的路径。
- 点击 “OK”,完成导出。
要不要包含比特流?
这是导出时唯一需要动脑的选择。官方规则是:以下情况必须包含比特流文件——
- 在设计中使用了 PL 端的逻辑资源。比特流要随 HPF 一起交给软件侧,最终由软件流程加载 PL。
- 使用了 Fast AHB 接口,需要传递码点信息(这一条仅针对包含 RISCV 的器件)。pl2ps 的码点信息也打包在比特流/HPF 里传递。
反过来说,如果你的设计纯粹只用 PS、PL 完全空闲(很少见),才可以选择不含比特流。拿不准就选包含,多带不会出错,少带必出问题。
保存位置与文件名
- 保存位置不限制,默认为 TD 的工程目录下。
- 文件名自动生成,规则为:
TD 工程名.hpf。 - 导出成功后会弹出提示窗口,里面标注了文件的保存位置,找不到文件时回来看这个提示即可。
拆包:HPF 里到底装了什么
HPF 文件的本质是一个 zip 压缩包——是的,你可以直接把后缀改成 .zip 解压,或者用解压软件打开。解压后的内容如下:

每个文件的职责见下表(保留原书表 3-1):
| 项目 | 文件名 | 描述 |
|---|---|---|
| 寄存器配置 | soc_plat.c | .c 和 .h 文件将用户在 IP 配置界面的配置项对应到寄存器配置,供 FSBL 使用 |
| 寄存器配置 | soc_plat.h | (与 soc_plat.c 配套的头文件) |
| 寄存器配置 | soc_plat.tcl | tcl 文件用于 OpenOCD 初始化 SoC |
| 配置结果可视化 | soc_plat.html | 通过表格的方式将配置结果呈现出来 |
| 硬件描述文件 | design.xml | 描述了硬件的器件信息、外部端口、接口、模块信息 |
| 比特流文件 | projectName.bit | FPGA 比特流文件 |
常见坑:把 HPF 当"黑盒"直接拷走就完事
HPF 不是给 FD “看一眼”的普通文件,软件侧真正消费的是包内的 soc_plat.c/.tcl。如果不理解每个文件的分工,很容易出现”APP 跑不起来、外设没反应”却查不到原因的情况——多数时候是 PS IP 压根没被初始化(见下一节)。
核心文件 soc_plat.c:PS IP 的寄存器初始化
HPF 中最重要的是 soc_plat.c 和 soc_plat.tcl 中的寄存器配置,作用是初始化 PS IP,让它进入正常工作状态。这一节只讲 .c/.h,.tcl 单独一节讲。
关键函数 Soc_PlatInit()
嵌入式开发环境(FD)的 SDK 中并没有隐式初始化 PS IP 的寄存器——SDK 不会替你做这件事,需要你显式在代码中调用:
Soc_PlatInit();这个函数定义在 soc_plat.c 中,把你在 IP 配置界面勾选的所有配置项,翻译成一条条寄存器写操作。
但注意适用范围:这种”代码里调用”的方式仅适用于运行在非 DDR 空间的 APP,例如跑在 TCM、OCM、FLASH 里的程序。逻辑是:程序自己管自己的初始化,上电先跑 Soc_PlatInit() 把 PS 配好,再继续跑业务代码。
如果 APP 运行在 DDR,则无需(也不能)在代码里加入该函数——因为 APP 要先被加载到 DDR 里才能运行,而加载 APP 本身就要求 DDR 已经处于正常工作状态。所以 DDR 的初始化必须发生在 APP 加载之前,由加载流程(如 FSBL)负责完成,轮不到 APP 自己来。
配置项 → 代码:七个部分
你在 PS IP 配置界面里的每一类配置,都会对应生成 soc_plat.c 中的代码段:
1. PSIO。PS 侧的 IO 具备复用功能,每个 IO 都要设置对应的参数和功能。配置界面长这样:

对于每一个 IO,都会生成对应的寄存器配置代码。
2. Clock。在 IP 界面中配置各个模块(CPU、SDIO 等)的时钟后,系统会自动生成 PLL 寄存器配置代码、各个模块时钟分频系数的代码:

3. PS_PL。使能 PS_PL 接口后,自动生成的主要是总线使能代码。配置界面如下:

生成的代码是标准的”寄存器写序列”格式,值得仔细读一遍,因为它揭示了这套机制的底层原理(原书图 3-6 代码):
/* (3) ps_pl data */
UL ps_pl_0[] = {
// Config PLS_PROT, gp normal access to pl
// register: PLS_PROT.gp_proten = 0x0
// 0xF8800080[1:1] = 0x0
CONFIG_REG_MASK(0xF8800080, 0x00000002, 0x00000000),
// Config CRG_EN, pl2ps max fbk info: 0
// register: FAHB_DPLL_PARA.fbk = 0x00000000
// 0xF8801058[31:24] = 0x00000000
CONFIG_REG_MASK(0xF8801058, 0xff000000, 0x00000000),
// Config CRG_EN, pl2ps max ref info: 0
// register: FAHB_DPLL_PARA.ref = 0x00000000
// 0xF8801058[23:16] = 0x00000000
CONFIG_REG_MASK(0xF8801058, 0x0ff0000, 0x00000000),
/* configuration complete */
CONFIG_REG_EXIT()
};读法:每条 CONFIG_REG_MASK(地址, 掩码, 值) 对应一次”按掩码写寄存器”的操作——注释里写明了目标寄存器名、位段和物理地址(如 0xF8800080 的 bit1),最后以 CONFIG_REG_EXIT() 标记配置序列结束。所谓”图形化配置 PS IP”,最终就是生成这样一张寄存器操作表。
4. DDR。系统按照 DDR 配置页面填的参数生成 DDR 初始化代码,用户配置的参数以宏的形式传递到 DDR 初始化代码中。
5. Peripherals。根据你选择的模块配置,自动生成某些外设正常工作所必需的代码。
6. Debug。根据 debug 模块的参数自动生成对应模块的初始化代码。
7. System。生成让 SoC 系统工作在最佳正常状态的初始化代码。
常见坑:忘了调用 Soc_PlatInit()
如果没有调用
Soc_PlatInit(),PS IP 未被初始化,某些外设的功能可能不正常——而且这种”不正常”往往不是完全瘫痪,而是个别外设行为诡异,非常难查。如果你的 APP 跑在 TCM/OCM/FLASH 且不经过 FD 的 Platform Init 流程,务必在 main 最早处调用它。
soc_plat.tcl 与 FD 调试:Run Platform Init
soc_plat.tcl 的内容与 soc_plat.c 功能完全一样,区别只是实现方式:它用 tcl 脚本去写 PS IP 的寄存器,供 OpenOCD 调用,实现在运行用户 APP 之前初始化 PS IP 寄存器。
使用入口在 FD 的调试配置里——勾选 “Run Platform Init” 功能后,FD 会在加载被调试(或运行)的 APP 之前,执行对应 Platform 工程下的 soc_plat.tcl 文件:

于是问题来了:初始化到底该放在 APP 代码里(Soc_PlatInit())还是放在调试器的 tcl 里(Run Platform Init)?官方给出的规则是二选一,绝不重复:
- 如果 APP 代码中没有调用
Soc_PlatInit()→ 调试时必须使能 Run Platform Init,否则 PS 没人初始化。 - 如果 APP 中显式调用了
Soc_PlatInit()→ 切勿使能 Run Platform Init,否则 PS IP 寄存器被二次初始化,某些外设会工作异常——手册特别点名了 DDR 这类对初始化时序敏感的模块。
可以画一张速查表:
| APP 位置 | 初始化方式 | Run Platform Init |
|---|---|---|
| 跑在 TCM/OCM/FLASH | 代码里调用 Soc_PlatInit() | 关闭 |
| 跑在 TCM/OCM/FLASH | 不调用,靠调试器 | 开启 |
| 跑在 DDR | 由加载流程在加载前完成 | 按 FD 工程实际配置,不能与代码内初始化叠加 |
soc_plat.html 与 driver:查配置、拿基地址
soc_plat.html:配置结果的人读版
该文件以 html 格式记录了 PS IP 的寄存器配置结果,用于配置结果的检查。用浏览器打开,就能以表格方式查看所有寄存器的配置。页面顶部有分类条目:PSIO TABLE、DDR、CLOCK、REGISTER VIEW,点击即可跳到对应的配置详情。调试寄存器问题时,先翻这个文件比反汇编 soc_plat.c 快得多。
driver:PL IP 的驱动信息
driver 文件夹中包含 PL IP 的驱动信息(仅针对 AXI 总线型 IP):该 IP 在 SoC 地址空间中的基地址、地址空间范围、中断号等信息。写裸机驱动或 Linux 设备树时,这些就是你 ioremap 和挂中断要用的参数。注意:该功能与软件版本有关,以你实际使用的版本为准。
通关标准:
- 能不看资料,在 TD 中完成”含 PS IP 工程导出 HPF”的全流程,并说出必须包含比特流的两种情况。
- 能把 .hpf 解压,说清 soc_plat.c / .h / .tcl / .html、design.xml、.bit 各自的角色。
- 能针对”APP 跑在 OCM”和”APP 跑在 DDR”两种场景,分别给出正确的 PS 初始化方案(代码内 or Platform Init),并解释为什么两者不能同时使用。
HPF 文件的本质是什么?可以怎么打开看内容?
HPF 本质是一个 zip 压缩包,包含 PS 端寄存器配置和 PL 端比特流文件。直接用解压软件(或改后缀为 .zip)解压,即可看到 soc_plat.c、soc_plat.h、soc_plat.tcl、soc_plat.html、design.xml 和 .bit 比特流文件。
为什么 APP 运行在 DDR 时,不能在代码里调用 Soc_PlatInit() 初始化 PS?
因为 APP 必须先被加载到 DDR 中才能运行,而加载动作本身就要求 DDR 已处于正常工作状态。所以 PS IP(包括 DDR 控制器)的初始化必须发生在 APP 加载之前,由 FSBL/加载流程完成,APP 代码里再调一次不仅无用,还可能造成二次初始化异常。
导出 HPF 时,"Export Hardware Platform File" 按钮是灰色的,最可能的原因是什么?
当前设计中不包含 PS IP。HPF 功能仅支持 FPSoC 系列芯片(如 DR1 系列),纯 FPGA 器件没有 PS IP,也就没有可导出的硬件平台信息。另外,设计中若包含两个及以上 PS IP 会直接报错。
勾选了 "Run Platform Init" 之后又调用了 Soc_PlatInit(),会发生什么?
PS IP 寄存器被二次初始化,某些外设会工作异常(手册点名 DDR)。正确做法是二选一:APP 代码里调用了 Soc_PlatInit() 就不要使能 Run Platform Init;没有调用则调试时必须使能它。
driver 文件夹里的信息对写驱动有什么用?
它记录了 AXI 总线型 PL IP 在 SoC 地址空间中的基地址、地址空间范围和中断号。写裸机驱动做寄存器映射(如 ioremap)或配置设备树中断时,这些参数直接从这里查。