第二章:开发环境搭建——交叉编译工具链配置、目标板系统镜像烧录、SSH/NFS调试环境搭建、串口终端与日志系统配置

说实话,很多刚入行的朋友觉得AI芯片驱动开发最难的是算法,是底层寄存器操作。但我个人经验告诉我,真正卡住你进度的,往往是开发环境没搭好。我见过太多人花了两周调一个驱动,最后发现是交叉编译工具链版本不对——这种坑,咱们今天一次性填平。

2.1 交叉编译工具链配置

为什么需要交叉编译?你想想看,你的开发机是x86架构,目标板是ARM或RISC-V。你不可能在目标板上装个GCC去编译——那玩意儿跑起来慢得让人抓狂。所以我们在PC上编译,生成目标板能跑的二进制文件。

我个人习惯用Linaro GCC,它对ARM架构支持最好。当然,如果你用的是华为昇腾或寒武纪的芯片,它们通常会提供定制版的工具链。

核心步骤:

  1. 下载对应架构的交叉编译工具链(比如gcc-arm-linux-gnueabihf)
  2. 解压到指定目录,例如 /opt/toolchains/
  3. 配置环境变量 PATH,让系统能找到交叉编译器
  4. 验证:运行 arm-linux-gnueabihf-gcc --version
# 以ARM Cortex-A72为例
export CROSS_COMPILE=arm-linux-gnueabihf-
export CC=${CROSS_COMPILE}gcc
export CXX=${CROSS_COMPILE}g++
export LD=${CROSS_COMPILE}ld
export AR=${CROSS_COMPILE}ar

# 验证
${CC} --version
# 输出类似:arm-linux-gnueabihf-gcc (Linaro GCC 7.5-2019.12) 7.5.0

我曾经踩过的坑:工具链的glibc版本必须与目标板系统镜像的glibc版本匹配。否则编译出来的程序一运行就报 FATAL: kernel too old 或者段错误。解决办法:用 readelf -l 你的程序 | grep INTERP 查看动态链接器路径,确认与目标板一致。

2.2 目标板系统镜像烧录

拿到一块新的AI开发板,第一件事就是给它灌系统。常见的烧录方式有:

  • SD卡烧录:用 dd 命令或 Etcher 工具,把镜像写入SD卡
  • USB烧录:通过厂商提供的烧录工具(如Rockchip的 RKDevTool
  • 网络烧录:通过TFTP/NFS从服务器加载内核和根文件系统

我个人最常用的是SD卡烧录,简单粗暴。但要注意:烧录前一定要确认镜像的校验和。我遇到过镜像下载过程中损坏,结果板子死活起不来,折腾了一天才发现是镜像本身的问题。

# 查看SD卡设备名(千万别搞错!)
lsblk

# 烧录镜像(假设SD卡是 /dev/sdb)
sudo dd if=ubuntu-20.04-arm64.img of=/dev/sdb bs=4M status=progress

# 同步并弹出
sync
sudo eject /dev/sdb

小技巧:烧录完成后,先别急着拔卡。挂载SD卡的boot分区,检查一下 config.txtextlinux.conf,确认内核启动参数是否正确。特别是 console=ttyS0,115200 这个参数,决定了你能不能通过串口看到启动日志。

2.3 SSH/NFS调试环境搭建

板子跑起来之后,你总不能每次都插着显示器和键盘吧?SSH和NFS就是你的远程调试利器。

SSH配置

说白了就是让板子开一个远程登录服务。我建议你一开始就配好静态IP,否则每次重启IP变了,你还得去查路由器。

# 在目标板上(通过串口或显示器登录后)
sudo apt update
sudo apt install openssh-server

# 配置静态IP(以Ubuntu为例)
sudo vi /etc/netplan/01-netcfg.yaml
# 内容示例:
network:
  version: 2
  ethernets:
    eth0:
      dhcp4: no
      addresses: [192.168.1.100/24]
      gateway4: 192.168.1.1
      nameservers:
        addresses: [8.8.8.8, 114.114.114.114]

sudo netplan apply

NFS挂载

为什么要用NFS?因为你在PC上改完代码,不用重新烧录,直接让板子通过网络挂载你的开发目录。这对于驱动开发来说,效率提升不是一点半点。

# 在PC上(NFS服务器)
sudo apt install nfs-kernel-server
sudo mkdir -p /home/yourname/nfs_root
sudo chmod 777 /home/yourname/nfs_root

# 编辑 /etc/exports,添加:
/home/yourname/nfs_root *(rw,sync,no_subtree_check,no_root_squash)

sudo exportfs -a
sudo systemctl restart nfs-kernel-server

# 在目标板上(客户端)
sudo mount -t nfs -o nolock 192.168.1.10:/home/yourname/nfs_root /mnt

注意:NFS挂载时加上 nolock 参数,很多嵌入式板子的NFS客户端不支持文件锁。不加这个参数,你可能会看到 mount.nfs: Stale file handle 错误。

2.4 串口终端与日志系统配置

串口是嵌入式开发的最后一道防线。当网络不通、系统崩溃时,串口是你唯一能跟板子对话的方式。

串口终端配置

PC端我用 minicomscreen。我个人更推荐 screen,因为它轻量、支持滚屏回看。

# 查看串口设备
ls /dev/ttyUSB*   # USB转串口
ls /dev/ttyS*     # 原生串口

# 用screen连接(波特率115200,8N1)
sudo screen /dev/ttyUSB0 115200

# 退出screen:Ctrl+A 然后按 K,再按 Y 确认

日志系统配置

驱动开发中,日志就是你的眼睛。我习惯在驱动代码里用 printk 分级输出:

// 驱动代码中的日志分级
#define pr_fmt(fmt) "my_driver: " fmt

#include <linux/printk.h>

// 不同级别的日志
pr_emerg("系统崩溃了!\n");   // 级别0,紧急
pr_alert("必须立即处理\n");   // 级别1,警报
pr_crit("严重错误\n");        // 级别2,临界
pr_err("出错了\n");           // 级别3,错误
pr_warn("警告一下\n");        // 级别4,警告
pr_notice("注意\n");          // 级别5,通知
pr_info("信息\n");            // 级别6,信息
pr_debug("调试信息\n");       // 级别7,调试

查看内核日志:

  • dmesg:查看所有内核日志
  • dmesg -w:实时监控日志输出
  • cat /proc/kmsg:直接读取内核日志缓冲区
  • tail -f /var/log/kern.log:查看持久化的内核日志

调试技巧:如果你发现 pr_debug 不输出,别慌。内核默认编译时会把调试信息去掉。你需要重新编译内核,打开 CONFIG_DYNAMIC_DEBUG 选项,或者直接在启动参数里加 loglevel=8 来提升日志级别。

知识体系总览

下面这张图,是我自己总结的开发环境搭建全流程。你照着这个顺序走,基本不会漏掉什么。

AI芯片驱动开发环境搭建流程 交叉编译工具链 Linaro GCC / 厂商定制版 系统镜像烧录 SD卡 / USB / 网络烧录 SSH/NFS调试环境 远程登录 / 文件共享 串口终端配置 minicom / screen 日志系统配置 printk / dmesg / loglevel 关键检查点 ✓ 工具链版本匹配 ✓ 镜像校验和正确 ✓ 静态IP配置 ✓ NFS nolock参数 ✓ 串口波特率115200 ✓ 日志级别设置 最终目标:PC上改代码 → 交叉编译 → NFS同步 → 目标板运行 → 串口看日志 一次配置,重复使用。别在环境搭建上反复折腾

嗯,到这里,开发环境这块基本就齐了。你可能会问:这些配置一次搞定以后就不用管了吧?其实不是。每次换芯片型号、换内核版本,工具链和镜像都得重新确认一遍。我自己的习惯是,每换一个项目,就把环境搭建的步骤写成脚本,下次直接跑脚本就行。

最后送你一句话:环境搭建花的时间,会在后面调试的时候十倍百倍地省回来。别嫌麻烦,一步到位才是最快的。


公众号:蓝海资料掘金营,微信deep3321