第二章:开发环境搭建

说实话,很多做固件开发的新人,上来就急着写代码。结果呢?编译不过、调试不了、版本乱成一锅粥。我见过太多这样的案例了。今天咱们就把开发环境这第一步走扎实。

2.1 Linux开发环境配置

做服务器芯片固件开发,Linux是标配。我个人习惯用Ubuntu 20.04 LTS,稳定,社区支持好。当然CentOS、Debian也行,看团队统一。

先说说基础依赖包。你想想看,一个干净的Linux系统,缺的东西可不少。我一般这么装:

sudo apt-get update
sudo apt-get install -y build-essential
sudo apt-get install -y git vim curl wget
sudo apt-get install -y libncurses5-dev libssl-dev
sudo apt-get install -y bison flex bc
sudo apt-get install -y python3 python3-pip
sudo apt-get install -y device-tree-compiler

嗯,这里要注意。不同芯片平台可能还有额外依赖。比如ARM平台需要gcc-arm-linux-gnueabihf,RISC-V平台需要riscv64-linux-gnu-gcc。别一股脑全装,看项目需求。

我的小技巧: 每次装完系统,我会写个shell脚本把依赖包都记录下来。下次换机器,直接跑一遍脚本,省时省力。

2.2 交叉编译工具链安装

交叉编译,说白了就是在PC上编译出能在目标芯片上跑的程序。为什么需要这个?因为服务器芯片的资源有限,你不可能在上面跑编译器。

我遇到过最坑的事:有人直接把PC上的gcc编译出来的二进制烧到芯片里,结果跑不起来。嗯,架构都不一样,怎么可能跑得起来?

安装交叉编译工具链,常见两种方式:

方式一:从芯片厂商获取

这是最推荐的方式。比如Intel的Slim Bootloader、ARM的GNU Toolchain。厂商会提供预编译好的工具链,直接解压就能用。

# 以ARM64为例
wget https://developer.arm.com/-/media/Files/downloads/gnu-a/10.3-2021.07/binrel/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu.tar.xz
tar -xf gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu.tar.xz
export PATH=$PATH:/path/to/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin

方式二:自己编译

如果你用的芯片比较小众,或者需要定制优化,那就得自己编译crosstool-NG。我当年为了一个MIPS的芯片,折腾了整整两天才把工具链搞定。

git clone https://github.com/crosstool-ng/crosstool-ng
cd crosstool-ng
./bootstrap
./configure --enable-local
make
./ct-ng menuconfig  # 配置目标架构
./ct-ng build
警告: 自己编译工具链很耗时,而且容易踩坑。除非必要,否则直接用厂商提供的。我曾经因为gcc版本不对,导致固件启动时内存对齐异常,查了三天才找到原因。

2.3 版本控制工具Git的使用

做固件开发,Git是基本功。我见过有人用U盘拷代码,结果版本搞混了,把稳定版覆盖了。那场面,啧啧。

这里我分享几个固件开发中常用的Git操作:

基本工作流

# 克隆仓库
git clone git@github.com:your-company/firmware.git

# 创建分支
git checkout -b feature/uart-driver

# 提交代码
git add .
git commit -m "feat: add UART driver for chip X"

# 推送到远程
git push origin feature/uart-driver

固件开发特有的Git技巧

固件开发经常要维护多个版本。比如芯片A的v1.0、v1.1,芯片B的v2.0。我习惯用tag来标记发布版本:

# 打标签
git tag -a v1.0.0 -m "Release version 1.0.0"
git push origin v1.0.0

# 查看历史
git log --oneline --graph --all

避坑指南: 我曾经在固件开发中犯过一个低级错误——直接在master分支上改代码。结果同事拉取代码后编译不过,整个团队等我修复。从那以后,我坚持「功能分支+Code Review」的流程。

2.4 固件调试工具准备

调试固件和调试普通程序不一样。你不能直接printf,因为芯片可能连串口都没初始化。你需要一套专业的调试工具链。

必备调试工具清单

工具名称 用途 我的推荐
OpenOCD 通过JTAG/SWD调试芯片 开源免费,支持多种调试器
GDB 源码级调试 配合OpenOCD使用,功能强大
UART工具 串口日志输出 minicom或screen,简单实用
逻辑分析仪 抓取硬件信号 Saleae Logic,性价比高
Trace工具 指令执行追踪 ARM的DSTREAM或Lauterbach

搭建调试环境

以OpenOCD+GDB为例,我一般这么配置:

# 安装OpenOCD
sudo apt-get install openocd

# 启动OpenOCD,连接调试器
openocd -f interface/jlink.cfg -f target/stm32f4x.cfg

# 另一个终端启动GDB
aarch64-none-linux-gnu-gdb firmware.elf
(gdb) target remote localhost:3333
(gdb) monitor reset halt
(gdb) load
(gdb) continue

我的调试心得: 刚开始做固件调试时,我总喜欢用print大法。后来发现,很多时候print本身就会改变程序行为(比如时序问题)。现在我的习惯是:先用逻辑分析仪看硬件信号对不对,再用GDB单步调试,最后才加print。这个顺序能省下不少时间。

串口调试配置

串口是固件开发者的「眼睛」。我习惯用screen工具:

# 查看串口设备
ls /dev/ttyUSB*

# 连接串口,波特率115200
screen /dev/ttyUSB0 115200

# 退出screen:Ctrl+A, 然后按K
注意: 串口波特率一定要和固件中配置的一致。我见过有人波特率设错了,然后怀疑芯片坏了。其实只是通信参数不匹配而已。

好了,开发环境搭建就讲到这里。下一章咱们开始实战——写第一个固件程序。到时候我会带着大家从零开始,把今天配置的环境用起来。