1. 课程导入

欢迎进入《RISC-V设备树与板级移植工程》课程。本课程面向已经具备一定嵌入式Linux基础、希望深入掌握RISC-V平台板级适配与设备树开发方法的工程师。在正式进入设备树语法、DTS编译、驱动匹配与板级调试之前,本章先建立一张“全局地图”:RISC-V架构的硬件特征是什么,设备树在RISC-V生态中承担什么职责,以及本课程从入门到实战的整体路径如何展开。

一、RISC-V架构概述:开放指令集带来的板级差异

RISC-V是一种开放、模块化的指令集架构(ISA)。与ARM、x86等由单一厂商主导的架构不同,RISC-V允许任何组织在遵循规范的前提下设计处理器核、SoC与开发板。这种开放性带来了生态繁荣,也带来了一个直接后果:硬件描述高度碎片化。同样是RV64GC核心,不同厂商的SoC在中断控制器、定时器、串口、时钟树、电源域、外设地址映射上可能完全不同。

从软件视角看,RISC-V平台通常包含以下关键组成:

  • Hart(硬件线程):RISC-V中的执行单元,可类比CPU核心或线程,支持多Hart并行。
  • 特权级:M模式(机器态)、S模式(监管态)、U模式(用户态),Linux通常运行在S模式,OpenSBI运行在M模式。
  • 中断与异常:本地中断由CLINT/ACLINT处理,外部中断常由PLIC或APLIC转发。
  • 定时器:mtime与mtimecmp提供计时基准,是调度与延时的基础。
  • 启动流程:上电后由BootROM加载SPL/OpenSBI,再引导U-Boot,最终启动Linux内核。

这些硬件差异如果全部硬编码进内核,将导致内核无法维护。因此,RISC-V生态普遍采用设备树作为“硬件描述与内核之间的契约”。

二、设备树在RISC-V生态中的角色

设备树(Device Tree)是一种描述硬件拓扑与资源的数据结构。它把“板子上有什么设备、设备挂在哪里、使用哪些地址与中断”从内核代码中剥离出来,交给引导程序在启动时传递给内核。对RISC-V而言,设备树的作用尤为关键,原因有三点:

  1. 解耦硬件与内核:同一份内核镜像可支持多块RISC-V开发板,只需更换DTB。
  2. 描述非可枚举总线:RISC-V SoC内部大量外设位于固定地址,无法像PCIe那样自动枚举,必须由设备树显式声明。
  3. 支撑驱动匹配:内核通过compatible属性匹配驱动,通过reg、interrupts、clocks等属性完成资源申请。
设备树不是“配置文件”,而是内核启动阶段解析的硬件描述。它决定了内核能否找到串口、网卡、存储控制器,也决定了驱动能否正确申请中断与时钟。

下面是一个RISC-V设备树片段的典型结构,展示了CPU、内存与串口的基本描述方式:

/dts-v1/;

/ {
    #address-cells = <2>;
    #size-cells = <2>;
    compatible = "vendor,board-name";
    model = "Vendor RISC-V Board";

    cpus {
        #address-cells = <1>;
        #size-cells = <0>;
        timebase-frequency = <10000000>;

        cpu@0 {
            device_type = "cpu";
            reg = <0>;
            compatible = "riscv";
            riscv,isa = "rv64imafdc";
            mmu-type = "riscv,sv39";
        };
    };

    memory@80000000 {
        device_type = "memory";
        reg = <0x0 0x80000000 0x0 0x40000000>;
    };

    soc {
        uart0: serial@10000000 {
            compatible = "ns16550a";
            reg = <0x0 0x10000000 0x0 0x100>;
            interrupts = <10>;
            clock-frequency = <100000000>;
        };
    };
};

可以看到,设备树用节点与属性描述了CPU数量、ISA扩展、内存基址、串口寄存器范围与中断号。内核启动时解析这些信息,进而完成平台初始化与驱动绑定。

三、板级移植工程的整体学习路径

本课程围绕“从拿到一块RISC-V开发板,到让Linux完整跑起来并适配外设”这一主线展开。整体学习路径可分为四个阶段:

阶段 核心任务 关键产出
基础认知 理解RISC-V架构、启动流程、设备树作用 能读懂DTS结构,理解DTB传递过程
设备树开发 编写DTS、编译DTB、调试节点与属性 可启动的最小设备树
板级适配 串口、时钟、中断、存储、网络适配 外设可用的完整板级支持
工程化与调试 多板复用、Overlay、启动优化、问题定位 可维护的板级移植方案

为了更直观地展示本课程的知识结构与板级移植工程中各环节的关系,下面给出本节的内联SVG图解。该图以RISC-V启动链路为主线,串联BootROM、OpenSBI、U-Boot、Linux内核与设备树,并展示设备树如何向各驱动模块提供硬件描述。

RISC-V 启动链路与设备树角色 从 BootROM 到 Linux 驱动,设备树贯穿始终 BootROM 上电首段代码 OpenSBI M模式固件 U-Boot 引导与DTB传递 Linux Kernel S模式内核 设备树 DTB CPU / 内存 / 中断 / 时钟 / 外设地址 传递 串口驱动 ns16550a 中断控制器 PLIC / APLIC 时钟与复位 clk / reset 存储与网络 MMC / Ethernet 目标:内核启动 + 外设可用 + 板级可维护

四、案例场景:一块新RISC-V开发板的移植起点

假设你拿到一块基于RV64 SoC的开发板,厂商只提供了原理图、芯片手册和一个可启动的U-Boot。此时Linux内核尚未适配。你的第一步不是改驱动,而是建立最小设备树:描述CPU、内存、串口和中断控制器。只有串口可用,才能看到内核启动日志;只有中断控制器正确,才能响应外设事件。

典型的最小启动验证命令如下:

# 编译设备树
make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- dtbs

# 查看生成的DTB
ls arch/riscv/boot/dts/vendor/board-name.dtb

# U-Boot中加载并启动
tftp 0x84000000 board-name.dtb
tftp 0x80200000 Image
booti 0x80200000 - 0x84000000

如果内核启动后串口无输出,优先检查设备树中的stdout-path、串口reg地址与clock-frequency,而不是急于修改驱动代码。

五、常见误区与复盘

误区一:把设备树当成“万能配置”。设备树只能描述硬件资源与连接关系,不能替代驱动逻辑。驱动缺失时,节点再多也无法工作。
误区二:忽略RISC-V启动链路的特权级切换。OpenSBI运行在M模式,Linux运行在S模式。设备树中的中断、定时器描述必须与固件实际配置一致,否则会出现中断丢失或时间异常。
复盘建议:每完成一个外设适配,都回到设备树中确认compatible、reg、interrupts、clocks四类属性是否与手册一致。建立“设备树—驱动—日志”三者对照表,是板级移植最有效的方法。