☰
飞腾CPU BIOS固件生成实战:交叉编译与平台配置全解析
2026/10/15 10:53:39 网站建设 项目流程

简介:面向飞腾FT-2000/4与D2000处理器的固件开发者,这份PDF教程系统讲解Uboot与UEFI BIOS固件的生成流程。教程从环境准备切入,推荐直接导入现成Ubuntu镜像来规避交叉编译环境搭建中的常见问题,同时也列出自行搭建所需的飞腾官方参考文档;随后逐步演示VMware虚拟机导入、硬件配置、共享文件夹设置、虚拟机登录,以及从宿主机复制源码和打包工具到虚拟机的操作方法。固件配置部分针对FT-2000/4与D2000分别给出Uboot与UEFI示例,并按DDR4内存条与颗粒类型分章节,说明源码文件、打包工具和适用场景的对应关系,便于按实际硬件查阅。资源为单个PDF文件,大小4.59MB,步骤完整、目录结构清楚,适合熟悉Linux基础操作但对飞腾平台尚不熟悉的开发者作为从零搭建固件编译环境的实操手册。已有7666人学习下载,值得参考。

1. 飞腾CPU BIOS固件生成到底在做什么:一次编译解决启动定制问题

很多人在拿到飞腾平台开发板或整机后,第一反应是直接用出厂烧好的固件,直到需要调整内存频率、修改串口配置或适配新的板载外设时,才发现手里只有编译好的二进制,没有可改的源码。飞腾CPU BIOS固件生成,就是把平台固件从源码重新构建成可烧录镜像的过程。这套流程在国产化替代、信创项目适配和硬件调试场景里非常常见,本质上跟普通UEFI固件构建没有太大区别,但因为飞腾的平台封装、配置文件和交叉编译链的版本匹配有自己的一套规则,第一次走通往往需要反复试错。

这篇笔记适合两类读者:一类是刚拿到飞腾开发板、想自己编译固件做启动定制的人;另一类是在做整机适配、需要把平台配置写进固件并反复出镜像的工程师。我会按实际构建顺序来讲:先理清源码结构和编译原理,再搭建环境、跑通编译、调整参数,最后把最容易翻车的地方逐个排掉。整个过程不需要你原本就懂UEFI内部实现,照着步骤走完,你手里会多一个自己生成的固件镜像,也会知道改哪些参数、看哪些日志来判断它是否正常工作。

2. 飞腾平台固件构建的技术链路:源码结构、交叉编译与产物关系

2.1 飞腾BIOS固件本质上是什么:UEFI源码、平台封装和可烧录镜像的关系

飞腾CPU平台上的BIOS固件,本质上是一个基于UEFI规范构建的固件镜像,而不是传统意义上的Legacy BIOS。它包含硬件初始化、内存训练、外设枚举和启动引导这几层逻辑。飞腾官方提供了一套平台源码包,里面既有通用的UEFI框架代码,也有针对飞腾处理器和配套芯片组的平台封装层。

需要先明确一个概念:你下载的所谓“BIOS源码”并不是一个可以单独编译出完整固件的独立工程。它通常分成三部分:第一部分是UEFI框架本身的源码,负责提供启动服务和驱动框架;第二部分是飞腾平台相关的Board Support Package,也就是板级支持包,包含CPU初始化、内存控制器配置、串口和中断控制器的底层代码;第三部分是平台配置文件,描述具体板型用什么内存颗粒、什么频率、串口映射到哪个物理地址。

在编译时,这三部分会被组合成一个固件描述文件(通常是FD文件),再经过工具链处理生成最终的固件镜像。理解这个关系很重要,因为后面很多编译报错和启动失败,根源都是这三者之间的版本不匹配,而不是某一行代码写错了。

2.2 交叉编译环境为什么必须匹配:工具链版本直接影响产物可用性

飞腾CPU是ARM架构的处理器(主要指FT-2000系列及后续平台),而我们日常用的构建机器一般是x86架构。这意味着你不能直接在x86机器上编译出ARM平台可执行的固件代码,必须使用交叉编译工具链。这个工具链负责把源代码编译成ARM指令集的二进制,再与平台相关的二进制文件打包在一起。

很多第一次接触的人会直接用系统包管理器安装一个通用的aarch64交叉编译器,结果编译到一半报出一堆莫名其妙的错误。这种情况绝大多数不是代码问题,而是工具链版本与源码包不匹配。飞腾平台源码包在发布时通常会锁定一个GCC版本和对应的库文件版本,锁定的原因是UEFI固件对编译器的代码生成方式有隐性依赖,例如某些内联汇编、内存屏障指令的生成格式在不同GCC版本之间有差异,而这种差异不会在编译时报错,只会在固件启动时表现为CPU异常或内存初始化失败。

我的建议是:先看源码包自带的构建脚本,里面通常会写明工具链的版本号。如果没有写明,就用源码包根目录下README或配置文件里推荐的版本。不要图省事用最新版GCC,也不要因为系统自带版本能编过就以为万事大吉。

2.3 源码目录里先看哪几个文件:从环境脚本到平台配置的阅读顺序

拿到源码包解压后,不要直接去找编译命令。先把目录结构过一遍,重点看下面几样东西,能省掉后面大量排查时间。

第一是环境变量脚本,一般叫env.sh或setenv.sh。它定义了交叉编译器的路径、编译工具链前缀、以及一些全局宏开关。编译前必须source这个脚本,否则你敲编译命令时工具链找不到。

第二是平台配置目录,一般在一个叫Platform或Board的目录下。里面有每个板型的专属配置目录,目录名通常就是板型代号。进入对应的板型目录,你会看到.dsc、.fdf和.inc这三种关键文件。.dsc文件定义了这个平台要编译哪些模块、打开哪些宏开关;.fdf文件定义了固件镜像的内存布局和分区;.inc文件是平台初始化配置,包括CPU频率、DDR频率、串口基地址等。

第三是构建脚本。飞腾平台一般会提供一个build.sh或类似的总控脚本,里面封装了调用UEFI构建系统的完整命令。你不需要从头理解UEFI构建系统的全部参数,但需要知道脚本里传递的平台名、工具链路径和输出目录这三个值。

下面是一个典型的构建脚本调用方式,我以一个常见的飞腾开发板为例:

# 进入源码根目录 cd /path/to/firmware-src # 加载编译环境变量 source env.sh # 设置编译工具链路径(按实际安装位置修改) export CROSS_COMPILE=aarch64-linux-gnu- export PATH=/opt/gcc-arm-8.3-aarch64/bin:$PATH # 调用平台构建脚本 ./build.sh ft2000_devboard

这里ft2000_devboard是平台名,build.sh会依据这个名字找到对应的.dsc和.fdf文件。CROSS_COMPILE指定交叉编译器的前缀,PATH里的目录必须包含实际的aarch64-linux-gnu-gcc可执行文件。如果平台名写错或者路径配错,构建系统会提示找不到平台配置,这在初期属于最高频的报错之一。

3. 搭建编译环境与准备源码:让最小构建跑通的第一步

3.1 构建机器的推荐配置与依赖包:怎样的系统环境能稳定跑完编译

编译飞腾固件的构建机器不需要很高配置,它不像编译操作系统内核那样吃资源,但有两件事必须注意:磁盘空间和依赖包完整性。固件源码解压后大概有几GB,编译过程中还会生成中间文件和临时镜像,建议预留至少20GB空间。内存建议8GB以上,低于4GB时编译大型模块可能会出现内存不足导致的进程被杀。

操作系统方面,常见的做法是用Ubuntu 18.04或20.04这类LTS版本,或者CentOS 7/8。不要用太新的发行版,因为新版系统自带的GCC版本太高,很可能与源码包期望的版本冲突。如果你只有新系统,那就需要额外安装老版本GCC并手动切换默认编译器。

依赖包方面,UEFI构建系统需要bison、flex、nasm、python和一些基础开发库。下面是在Ubuntu系统上安装依赖的常见做法:

# 更新软件源索引 sudo apt update # 安装编译固件所需的基础依赖 sudo apt install -y build-essential bison flex nasm python3 \ uuid-dev python3-distutils # 确认python版本(构建脚本可能依赖python2或python3,具体看脚本) python3 --version

build-essential提供GCC和make,bison和flex是UEFI构建系统解析配置文件的语法分析器,nasm是汇编器,uuid-dev提供生成GUID所需的头文件。如果你用的是CentOS,对应命令是yum install,包名略有差异但功能一致。这里需要特别说明:nasm是x86汇编器,UEFI构建系统在生成某些通用启动模块时会用到它,即使目标是ARM平台也不能省略。

3.2 设置编译环境变量:一条命令验证交叉工具链是否可用

环境变量是飞腾固件编译中最容易出问题的地方。很多人的编译失败不是因为代码有问题,而是因为工具链路径没加进PATH,或者CROSS_COMPILE前缀与实际安装的交叉编译器对不上。

设置环境变量的常见做法有两种:一种是每次编译前手动执行export命令,另一种是把变量写进~/.bashrc让它自动加载。我习惯用前者,因为不同项目可能用不同版本的交叉编译器,写在~/.bashrc里反而容易互相干扰。

设置完环境变量后,建议你先验证一下工具链是否真的可用,再开始编译,避免编译到一半才发现编译器路径错了:

# 查看交叉编译器的完整路径 which aarch64-linux-gnu-gcc # 查看编译器版本 aarch64-linux-gnu-gcc --version # 用工具链编译一个最简单的C文件验证可用性 echo 'int main(){return 0;}' > test.c aarch64-linux-gnu-gcc -o test test.c file test

最后一条file test会输出这个可执行文件的格式信息。如果输出里出现ARM aarch64字样,说明交叉工具链工作正常;如果输出显示x86-64,说明你实际调用的不是交叉编译器,PATH顺序有问题。

这里有个很隐蔽的坑:有些系统同时装了多个交叉编译器,which命令返回的路径不一定是你想要的。建议用绝对路径调用,或者在export PATH时把目标工具链目录放在最前面。我之前在这上面栽过跟头,明明--version显示版本正确,但编出来的固件在板子上完全起不来,后来发现是另一个路径下的旧版编译器被优先调用了。

3.3 平台配置文件中需要关注的初始参数:板型、DDR频率与串口映射

在跑第一次编译前,我建议你先打开自己板型对应的平台配置文件,把几个关键参数确认一遍。这样即使第一次编译失败,你也能判断是环境问题还是配置问题。

第一个要确认的是板型选择。在.dsc文件里通常有一个宏叫PLATFORM_NAME或类似的字段,它必须与你板子实际型号一致。选错板型会导致生成的内存时序参数和外设初始化代码不匹配,轻则启动慢,重则直接死机。

第二个要确认的是DDR频率。飞腾平台的DDR控制器初始化参数在.inc文件中,通常以DDR_FREQ或类似的宏出现。默认值一般是板厂调试好的稳定频率,如果你不确定自己板子的内存颗粒支持什么频率,不要动它。改这个参数是后期微调的事,第一次编译应该用默认值跑通流程。

第三个要确认的是串口基地址。调试串口的物理地址如果与你的板子实际布线不一致,就会遇到固件烧进去后串口完全没有输出的情况。这个值通常在.inc文件里以UART_BASE或DEBUG_UART_BASE的形式出现。

下面是我在一个飞腾平台.inc文件中见到的常见配置写法,用于说明参数长什么样:

# DDR控制器配置 define DDR_FREQ = 2400 # DDR频率,单位MHz # 调试串口配置 define UART_BASE = 0x28000000 # 串口0的物理基地址 # CPU核心配置 define CORE_NUM = 8 # 启用的核心数量

注意这些字段在不同源码版本里的命名可能有差异,但含义是共通的。你把板子的实际硬件信息与这些参数对照一遍,就能在编译前发现大部分配置错误。比如板子上串口芯片接在某个特定地址,而配置文件里写的是另一个地址,那烧进去后必然没有日志输出。

4. 生成飞腾BIOS固件镜像:编译命令、产物路径与参数调整

4.1 完整编译命令与输出产物:从源码到FDK镜像的对应关系

环境准备好、平台配置确认过之后,就可以开始第一次编译了。飞腾固件的编译过程由UEFI构建系统驱动,你不需要手动执行每一行底层命令,只需要调用总控脚本或构建系统的入口命令。

常见的做法是执行源码包根目录下的build.sh,或者直接调用UEFI构建系统的build命令配合平台参数。下面以调用build.sh为例,展示一次完整的最小构建:

# 切换到源码根目录 cd /path/to/firmware-src # 加载环境变量(必须,否则找不到工具链) source env.sh # 执行完整构建,V=1表示输出详细信息 ./build.sh ft2000_devboard V=1 2>&1 | tee build_log.txt

命令里的V=1是UEFI构建系统的常见参数,表示显示完整编译命令。tee build_log.txt的作用是把编译输出同时写到日志文件,方便后面排查问题。第一次编译时间取决于机器性能,一般在十几分钟到半小时之间。

编译完成后,固件产物通常在源码目录下的Build或Output目录中。需要关注的文件有三个:第一个是FV目录下的固件卷镜像,文件名类似FVMAIN.FV,这是固件的主体;第二个是FD目录下的固件描述文件镜像,通常以.fd结尾,这是最终烧录到SPI Flash的完整镜像;第三个是MAP文件,记录每个模块在固件中的地址布局,排错时很有用。

如果你的构建脚本输出中没有生成.fd文件,说明配置有问题或某个模块编译失败。这时候回头检查编译日志末尾的ERROR信息,重点看是哪个模块报错,以及报错是语法层面还是链接层面的。

4.2 编译选项与生成参数的调整:编译宏开关与平台配置覆盖方法

固件编译的灵活性体现在你可以通过修改.dsc文件中的宏开关来决定哪些模块编进固件、哪些不编。常见的调整场景包括:关闭不必要的驱动模块来减小固件体积、打开调试日志模块来输出更详细的启动信息、调整串口波特率等。

修改宏开关的方式是编辑.dsc文件,找到对应模块的DEFINE语句。下面以打开调试日志输出为例:

# 在xxx.dsc文件中,找到模块配置区域 # 原始状态一般是: # DEFINE DEBUG_ENABLE = FALSE # 改为: DEFINE DEBUG_ENABLE = TRUE

修改后需要重新编译,固件镜像中就会包含调试日志输出代码。日志输出参数也可以调整,比如把默认的串口波特率从115200改成更高的值:

# 常见串口参数配置写法(实际字段名以源码为准) DEFINE UART_BAUD_RATE = 115200

修改.dsc或.fdf文件后,建议删除旧的Build目录再重新编译。UEFI构建系统虽然会做增量编译,但它对配置文件变更的检测并不总是可靠,保留旧中间文件有时会导致你改了配置却不生效。这是一个很典型的“改了没用,其实是没清干净”的场景。

4.3 固件烧录与验证:生成镜像后如何确认它真的能用

固件编译完成后,烧录方式和验证手段是决定这个镜像是否可用的关键环节。飞腾平台的固件一般烧录到板载SPI Flash中,烧录工具因板型而异,常见的有基于Linux下flashrom的烧录方式和板厂自行提供的专用烧录工具。

烧录前建议先把原厂固件备份一份,这相当于买了一份后悔药。操作方式是先用烧录工具读取当前Flash内容保存为备份文件,再写入新固件。下面以flashrom为例展示备份和烧录命令的常见形式:

# 备份当前固件到文件 flashrom -p dummy -r backup_bios.bin # 写入新编译的固件镜像 flashrom -p dummy -w Build/ft2000_devboard/FD/xxx.fd

这里的-p dummy只是示意,实际写板子时需要根据Flash芯片型号指定编程器名称,比如-p linux_spi或板厂专用的-p ft_spi。烧录完成后,连接调试串口并上电,观察启动日志是否正常输出,这是最直接的验证方式。

第一次验证时不要急着看完整启动流程,只需要确认三件事:串口是否有输出、固件版本号是否是你编译时打的时间戳、是否进入了预期的启动阶段。如果串口无输出,优先检查串口接线、基地址配置和波特率设置,不要怀疑是固件编译错了。

5. 飞腾CPU BIOS固件生成避坑指南:5个高发问题与排查步骤

5.1 编译报错提示找不到GCC编译器

现象:执行构建脚本后,终端输出gcc: command not found或aarch64-linux-gnu-gcc: command not found,构建立刻中止。

原因有两个层面。第一层是PATH环境变量没有包含交叉编译器所在目录,系统找不到可执行文件;第二层是env.sh脚本里的工具链路径写死了一个不存在的目录,或者CROSS_COMPILE前缀与实际的交叉编译器命名不一致。

解决办法是直接检查环境变量和实际文件路径的对应关系。执行echo $PATH查看当前路径列表,确认交叉编译器目录是否在其中;再执行ls /opt/gcc-arm-8.3-aarch64/bin/看目录下实际有哪些可执行文件。如果实际的可执行文件叫aarch64-linux-gcc而脚本里写的是aarch64-linux-gnu-gcc,改脚本或做软链接都可以,但建议改脚本,因为你不会想在每个环境里都手动建软链接。

5.2 编译能通过,但烧录后板子完全无响应

现象:编译过程没有任何报错,固件也正常生成,但烧录后上电,串口无输出,板子像死了一样。

原因往往不在编译环节,而在平台配置或工具链版本上。最常见的原因是工具链版本与源码包不匹配,编译器生成的指令在目标CPU上触发了异常,而异常发生在串口初始化之前,所以你看不到任何日志。其次是.dsc文件里的DEBUG_PORT或串口基地址配置与实际板卡不一致。

解决办法是先用原厂固件确认板子本身是正常的,这一步可以做交叉验证。然后检查你使用的GCC版本是否严格匹配源码包要求。如果版本没问题再看串口基地址。不要一上来就怀疑烧录工具问题,除非你确认了原厂固件烧回去也是同样现象。

5.3 串口有输出但全是乱码

现象:板子能启动,串口也有数据,但显示的全是乱码,无法阅读日志内容。

原因基本锁定在串口波特率不匹配。飞腾平台固件默认波特率一般写在平台配置中,常见值是115200,但也有板厂在出厂时改成了其他值比如1500000。你的串口工具如果用115200去读一个1500000的输出,必然是乱码。

解决办法是先用万用表或示波器确认串口电平是否正常(这一步新手容易忽略,但很有必要),然后尝试切换串口工具的波特率。如果确认波特率正确仍然乱码,检查你的USB转串口模块是否支持目标波特率,部分廉价转换模块在高波特率下会丢数据或产生时钟偏差。

5.4 改了DDR频率参数但编译后没有任何变化

现象:在配置文件中修改了DDR频率为更高值,重新编译烧录后运行,用内存测试工具查看频率,发现数值没变。

原因是增量编译没有把平台初始化代码重新构建。UEFI构建系统的依赖跟踪并不完善,特别是对.inc这类被间接包含的配置文件,修改后构建系统可能不会感知到文件变化。

解决办法是删除Build目录下对应的平台目录,强制全量重建。如果你不确定清哪些文件,最简单粗暴的做法是把整个Build目录删掉,重新执行完整编译。这个过程多花十几分钟,但能保证配置一定生效。另外一个相关坑是:DDR频率不是想改就能改的,它与内存颗粒型号、PCB布线长度、电源供电能力都相关,改上去起不来是正常现象。

5.5 固件启动到一半死机,日志停在同一位置

现象:启动日志输出了几十行后停止,板子没有继续运行也没有重启。

原因分两类。一类是外设初始化失败,比如某个PCIe设备没有正确枚举,或者某个I2C设备没有响应,代码进入等待超时流程但超时时间很长。另一类是内存配置不稳定,系统在后续内存访问时触发了总线错误。

解决办法是先看日志最后一行停在了哪个模块。在UEFI启动流程中,日志通常会在每个模块入口打印一条信息。停在哪个模块,问题大概率就在那个模块或其依赖的外设上。先把板子上能拔的外设都拔掉,只保留最小系统再试一次。如果最小系统能通过,逐个插回外设定位问题。如果最小系统仍然死机,重点检查内存频率和时序参数,把DDR频率降一档再试。

6. 生成固件后的三个验证习惯:从日志、版本到启动阶段的确认方法

6.1 启动日志里重点看哪几行:从固件版本到平台初始化完成标志

固件编好烧进去之后,不要只看“能启动”就收工。我一般会在日志里找三个标志性的输出位置。第一个是固件版本号和时间戳,确认当前运行的确实是你刚编译的镜像,而不是之前残留的旧固件。第二个是DDR控制器初始化完成的信息,通常包含内存大小和频率,确认与配置一致。第三个是“进入Boot Device Select”或类似表示UEFI启动流程已经走到加载启动项阶段的日志,这表示固件主流程完整走通了。

这三个标志都确认无误,基本可以判定固件生成和烧录成功。如果时间戳正确但内存频率不对,说明配置没生效,回头看增量编译清理的坑。如果版本号是旧的,说明烧录没成功或者写了错误的文件路径。

6.2 用版本时间戳和MAP文件交叉验证固件身份

当你手里有多个版本的固件文件时,很容易搞混哪个是最新的。我的习惯是每次编译前先改一个版本字符串,这个字符串一般在.dsc文件或某个固件版本头文件中定义。改成包含日期的格式,比如FT_DEVBOARD_V2.3_20250115,这样编译出来的固件在启动日志里会直接显示这个字符串,一眼就能认出是哪个版本。

另一个验证手段是MAP文件。固件编译生成的.map文件记录了每个函数和模块在最终镜像中的地址。当你怀疑某个修改没有编进去时,可以在MAP文件里搜索对应的符号,看它的地址和相邻模块的相对位置是否变化。虽然MAP文件对普通调试来说用处不大,但在确认特定补丁是否生效时非常有效。

6.3 一个值得长期保持的构建习惯:每次编译保留一份日志和产物副本

我自己的教训是:不要覆盖编译日志和产出固件。每次编译完成后,把构建日志、.fd镜像文件和当时的.dsc配置复制到一个以时间或版本号命名的目录中。这不是形式主义,而是因为固件问题的排查往往需要回溯——当你改了五次配置后板子起不来了,你能快速对比是哪一次改动引入的问题,而不是重新编译所有版本去猜。

具体的做法很简单,编译成功后执行一条打包命令:

# 记录当前配置和产物到带时间戳的目录 mkdir -p ./build_archive/$(date +%Y%m%d_%H%M) cp build_log.txt ./build_archive/$(date +%Y%m%d_%H%M)/ cp Build/ft2000_devboard/FD/xxx.fd ./build_archive/$(date +%Y%m%d_%H%M)/ cp Platform/ft2000_devboard/xxx.dsc ./build_archive/$(date +%Y%m%d_%H%M)/

这个习惯看似多花了一点时间和磁盘空间,但在实际项目里已经不止一次帮我挽救了局面。有一次某开发者改完配置后板子起不来,就是靠三小时前备份的上一版配置和固件做对比,确定了是新改的内存时序参数导致的失败。现在每次做固件迭代,我都会在编译前想一遍这句话:这次如果翻车了,我拿什么来对比定位问题。希望这套流程也能帮到你,让你的固件生成不再像个黑匣子。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询