说明:本文内容节选自小梅哥编写的《SoC FPGA嵌入式设计和开发教程》一书第9章9.1和9.2节内容。

在“Step by Step为HPS添加UART外设”章节,我们讲解了如何使用SoC EDS软件为创建好的包含HPS的Qsys系统添加UART外设并生成相应的设备树(dts)文件。在“基于Linux应用程序的HPS配置FPGA”章节,我们也提到了使用开发软件安装包提供的不含FPGA逻辑部分的设备树文件来配合启动Linux系统。那么什么是设备树?如何得到适配硬件系统的设备树?linux系统又是如何使用设备树信息来加载各种设备驱动的呢?本节将针对上述问题,以一个具体的实例,讲解设备树的运用。

9.1 什么是设备树

在讲到设备树之前,先看一个具体的应用场景。对于一个ARM处理器,一般其片上都会集成了有较多的外设接口,包括I2C、SPI、UART等。而I2C、SPI都属于总线属性,在这些总线上又会连接其它的外部器件。例如在I2C总线上,又会连接EEPROM、I2C接口的各种传感器、LCD显示屏、RTC等。那么Linux系统如何能够知道I2C总线上连接了哪些设备?又如何知道这些设备的具体信息呢?

在早期的Linux系统中,使用的是硬件描述文件的形式来实现该功能的。每一个具体的硬件平台都会在Linux系统源码包的arch/arm/mach-xxx/目录下存在一个硬件信息描述的源码包,在该源码包中定义了GPIO的使用、外设、i2c总线等系统信息。如果对某个硬件平台进行了修改,例如将EEPROM的容量从16Kb更换为了64Kb,或者在I2C总线上新增了一个从机,则需要修改对应的硬件描述文件,然后重新编译内核。

在arch/arm/下定义了很多mach-xxx的文件夹,一般是按照厂商或者平台命名,例如高通平台的为mach-msm,marvell的为mach-mmp,mach-pxa。

随着新的硬件平台不断产生,为了支持这些硬件平台,Linux系统中会增加越来越多的板级描述文件,从而导致系统中的冗杂文件越来越多。

为了解决这个问题,Linux内核从3.x开始引入设备树的概念,用于实现驱动代码与设备信息相分离。在设备树出现以前,所有关于设备的具体信息都要写在驱动里,一旦外围设备变化,驱动代码就要重写。引入了设备树之后,驱动代码只负责处理驱动的逻辑,而关于设备的具体信息存放到设备树文件中,这样,如果只是硬件接口信息的变化而没有驱动逻辑的变化,驱动开发者只需要修改设备树文件信息,不需要改写驱动代码。

比如在ARM Linux内,一个.dts(device tree source)文件对应一个ARM的machine,一般放置在内核的"arch/arm/boot/dts/"目录内,比如友晶的DE0-nano-SoC开发板就是"arch/arm/boot/dts/socfpga_cyclone5_de0_sockit.dts"。这个文件可以通过$make dtbs命令编译成二进制的.dtb文件供内核驱动使用。

基于同样的软件分层设计的思想,由于一个SoC可能对应多个machine,如果每个machine的设备树都写成一个完全独立的.dts文件,那么势必相当一些.dts文件有重复的部分,为了解决这个问题,Linux设备树目录把一个SoC公用的部分或者多个machine共同的部分提炼为相应的.dtsi文件。这样每个.dts就只有自己差异的部分,公有的部分只需要"include"相应的.dtsi文件, 这样就使整个设备树的管理更加有序。例如,对于Intel的SoC FPGA器件,其包括Cyclone V、Arria V、Arria 10三个系列,这三个系列中,有很多内容是相同的,可以作为公共部分,因此在linux源码中,使用了socfpga.dtsi文件来描述所有socfpga器件通用的部分,然后针对Cyclone V、Arria V、Arria 10这三个系列,又分别使用了socfpga_cyclone5.dtsi、socfpga_arria5.dtsi、socfpga_arria10.dtsi三个文件来描述各个系列的硬件中公共的部分。当具体到某个特定的硬件板卡,如DE0-nano-SoC开发,其设备树文件socfpga_cyclone5_de0_sockit.dts正文的第一行就是使用了#include"socfpga_cyclone5.dtsi"来包含cyclone5器件的通用部分,而在socfpga_cyclone5.dtsi文件中,正文的第一行又是使用了#include "socfpga.dtsi"来包含所有socfpga器件的通用部分。通过这种方式,简化了设备树的构成。

9.2 设备树基本格式

设备树用树状结构描述设备信息,它有以下几种特性

  1. 每个设备树文件都有一个根节点,每个设备都是一个节点。

  2. 节点间可以嵌套,形成父子关系,这样就可以方便的描述设备间的关系。

  3. 每个设备的属性都用一组key-value对(键值对)来描述。

  4. 每个属性的描述用“;”结束

为了方便分析,这里以“Step by Step为HPS添加UART外设”章节中生成的soc_system.dts文件的内容为例,介绍设备树文件的基本格式。

soc_system.dts文件中开头部分内容如下所示:

0008      / {

0009             model = "Altera SOCFPGA  Cyclone V";

0010             compatible =  "altr,socfpga-cyclone5", "altr,socfpga";

0011             #address-cells = <1>;

0012             #size-cells = <1>;

0013             height  = <2>;      /* appended from  boardinfo */

0014             width = <16>;     /* appended from boardinfo */

0015             brightness = <8>;       /* appended from boardinfo */

0016             pagesize = <32>; /* appended from boardinfo */

0017

0018             aliases {

0019                    ethernet0 = "/sopc@0/ethernet@0xff702000";

0020             }; //end aliases

0021

0022             cpus {

0023                    #address-cells =  <1>;

0024                    #size-cells = <0>;

0025                    enable-method =  "altr,socfpga-smp";

0026

0027                    hps_0_arm_a9_0: cpu@0x0 {

0028                           device_type =  "cpu";

0029                           compatible =  "arm,cortex-a9-17.1", "arm,cortex-a9";

0030                           reg =  <0x00000000>;

0031                           next-level-cache =  ;

0032                    }; //end cpu@0x0  (hps_0_arm_a9_0)

0033

0034                    hps_0_arm_a9_1: cpu@0x1 {

0035                           device_type =  "cpu";

0036                           compatible =  "arm,cortex-a9-17.1", "arm,cortex-a9";

0037                           reg =  <0x00000001>;

0038                           next-level-cache =  ;

0039                    }; //end cpu@0x1  (hps_0_arm_a9_1)

0040             }; //end cpus

0041

0042             memory {

0043                    device_type =  "memory";

0044                    reg = <0xffff0000  0x00010000>,

0045                           <0x00000000  0x80000000>;

0046             }; //end memory

第8行,一个“/”表示一个硬件平台,该硬件平台有以下属性

  • model:产品型号,为AlteraSOCFPGA Cyclone V。

  • compatible:兼容属性,用来描述产品与Linux系统中支持的哪个平台兼容。

  • height、width、brightness:这些属性用于描述板上某专用硬件的一些物理信息,例如这里的height为2、width为16,实际上是描述了Inte原厂开发板上提供的LCD显示屏的显示高度和宽度,AC501-SoC开发板上并未设置该LCD显示屏,但是该部分硬件我们依旧保留在了hps_common_board_info.xml文件中,方便读者参考学习。

第18行~20行,描述了一个基本的以太网节点信息, ethernet@0xff702000表示该以太网位于绝对地址为0xff702000的位置,而根据Cyclone V 器件手册,0xff702000这个地址正是EMAC1的绝对地址。

第22行~40行,cpus节点,描述了该开发板上的CPU节点信息。在SoC FPGA器件中,包含了两个Cortex-A9的CPU,因此在cpus节点中又包含了两个子节点,分别名为hps_0_arm_a9_0和hps_0_arm_a9_1。

再如第88行~195行:

0088             sopc0: sopc@0 {

0089                    device_type =  "soc";

0090                    ranges;

0091                    #address-cells =  <1>;

0092                    #size-cells = <1>;

0093                    compatible =  "ALTR,avalon", "simple-bus";

0094                    bus-frequency = <0>;

0095

0096                    hps_0_bridges:  bridge@0xc0000000 {

0097                           compatible =  "altr,bridge-17.1", "simple-bus";

0098                           reg =  <0xc0000000 0x20000000>,

0099                                  <0xff200000  0x00200000>;

0100                           reg-names =  "axi_h2f", "axi_h2f_lw";

0101                           clocks =  ;

0102                           clock-names =  "h2f_axi_clock", "h2f_lw_axi_clock";

0103                           #address-cells =  <2>;

0104                           #size-cells =  <1>;

0105                           ranges =  <0x00000001 0x00000000 0xff200000 0x00000008>,

0106                                  <0x00000001  0x00000100 0xff200100 0x00000080>,

0107                                  <0x00000001  0x00010000 0xff210000 0x00000008>,

0108                                  <0x00000001  0x00010040 0xff210040 0x00000020>,

0109                                  <0x00000001  0x000100c0 0xff2100c0 0x00000010>,

0110                                  <0x00000001  0x00000060 0xff200060 0x00000020>,

0111                                   <0x00000001  0x00000020 0xff200020 0x00000020>,

0112                                  <0x00000001  0x00000040 0xff200040 0x00000020>;

0113

0114                           i2c_0:  unknown@0x100000000 {

0115                                  compatible =  "unknown,unknown-1.0";

0116                                  reg =  <0x00000001 0x00000000 0x00000008>;

0117                                  interrupt-parent  = ;

0118                                  interrupts =  <0 41 4>;

0119                                  clocks =  ;

0120                           }; //end  unknown@0x100000000 (i2c_0)

0121

0122                           alt_vip_vfr_tft:  vip@0x100000100 {

0123                                  compatible =  "ALTR,vip-frame-reader-14.0",  "ALTR,vip-frame-reader-9.1";

0124                                  reg  = <0x00000001 0x00000100 0x00000080>;

0125                                  clocks =  ;

0126                                  max-width =  <800>;  /

0127                                  max-height =  <480>;

0128                                  bits-per-color  = <8>;  /*

0129                                  colors-per-beat  = <4>;       /*

0130                                  beats-per-pixel  = <1>;

0131                                  mem-word-width  = <128>;

0132                           }; //end  vip@0x100000100 (alt_vip_vfr_tft)

0133

0134                           sysid_qsys:  sysid@0x100010000 {

0135                                  compatible =  "altr,sysid-17.1", "altr,sysid-1.0";

0136                                  reg =  <0x00000001 0x00010000 0x00000008>;

0137                                  clocks =  ;

0138                                  id  = <2899645186>;

0139                                  timestamp =  <1532912636>;   /*

0140                           }; //end  sysid@0x100010000 (sysid_qsys)

0141

0142                           led_pio:  gpio@0x100010040 {

0143                                  compatible =  "altr,pio-17.1", "altr,pio-1.0";

0144                                  reg =  <0x00000001 0x00010040 0x00000020>;

0145                                  clocks =  ;

0146                                  altr,gpio-bank-width  = <2>;     /*

0147                                  resetvalue =  <0>; /*

0148                                  #gpio-cells  = <2>;

0149                                  gpio-controller;

0150                           }; //end  gpio@0x100010040 (led_pio)

0151

0152                           button_pio:  gpio@0x1000100c0 {

0153                                  compatible =  "altr,pio-17.1", "altr,pio-1.0";

0154                                  reg =  <0x00000001 0x000100c0 0x00000010>;

0155                                  interrupt-parent  = ;

0156                                  interrupts =  <0 43 1>;

0157                                  clocks =  ;

0158                                  altr,gpio-bank-width  = <2>;     /*

0159                                  altr,interrupt-type  = <2>;  /*

0160                                  altr,interrupt_type  = <2>;  /*

0161                                  edge_type =  <1>;       /*

0162                                  level_trigger  = <0>;   /*

0163                                  resetvalue =  <0>; /*

0164                                  #gpio-cells  = <2>;

0165                                  gpio-controller;

0166                           }; //end  gpio@0x1000100c0 (button_pio)

0167

0168                           uart_0:  serial@0x100000060 {

0169                                  compatible =  "altr,uart-17.1", "altr,uart-1.0";

0170                                  reg =  <0x00000001 0x00000060 0x00000020>;

0171                                  interrupt-parent  = ;

0172                                  interrupts =  <0 44 4>;

0173                                  clocks =  ;

0174                                  clock-frequency  = <50000000>;     /*

0175                                  current-speed  = <115200>;      /*

0176                           }; //end  serial@0x100000060 (uart_0)

0177

0178                           uart_1:  serial@0x100000020 {

0179                                  compatible =  "altr,uart-17.1", "altr,uart-1.0";

0180                                  reg = <0x00000001  0x00000020 0x00000020>;

0181                                  interrupt-parent  = ;

0182                                  interrupts =  <0 42 4>;

0183                                  clocks =  ;

0184                                  clock-frequency  = <50000000>;     /*

0185                                  current-speed  = <115200>;      /*

0186                           }; //end  serial@0x100000020 (uart_1)

0187

0188                           spi_0:  spi@0x100000040 {

0189                                  compatible =  "altr,spi-17.1", "altr,spi-1.0";

0190                                  reg =  <0x00000001 0x00000040 0x00000020>;

0191                                  interrupt-parent  = ;

0192                                  interrupts =  <0 40 4>;

0193                                  clocks = ;

0194                           }; //end  spi@0x100000040 (spi_0)

0195                    }; //end bridge@0xc0000000  (hps_0_bridges)

该部分首先是在第88行描述了一个名为sopc的节点,而在该节点下,又包含了一个名为hps_0_bridges的子节点,该节点表示了"axi_h2f"和 "axi_h2f_lw"两个HPS到FPGA的通信桥。在该通信桥节点上,又描述了I2C控制器(i2c_0)、FrameReader控制器(alt_vip_vfr_tft)、设备ID(sysid_qsys)、基于PIO的LED控制器(led_pio)、基于PIO的按键控制器(button_pio)、串口控制器(uart_0、uart_1)、spi控制器(spi_0)。这些节点所代表的设备正是我们在Platform Designer中添加的FPGA侧的IP。因此,如果我们在FPGA侧增加、删除、修改了某些IP,然后使用SoC EDS软件重新生成dts文件,这些变化也都会体现在hps_0_bridges节点下。例如我们修改添加的uart_1控制器的默认波特率为9600bps,然后重新生成dts文件,则可以看到dts文件中uart_1节点下的current-speed属性值会从115200变为9600。用户也可以对比AC501_SoC_GHRD工程生成的dts文件,是没有uart_1这个节点的,只有在经过了“Step by Step为HPS添加UART外设”实验后得到的新工程生成的dts文件,才有uart_1节点。

了解了什么是设备树,那么,设备树又是怎么指导linux内核加载指定驱动的呢,或者说,Linux的驱动程序是怎么样确定系统中有这个硬件,以及该硬件的具体参数,并完成驱动的加载和设置的呢?具体内容,可以关注小梅哥“9 Linux设备树的原理与应用实例”系列文章的下一篇文章“Linux设备树应用实例”。

新书预告

由小梅哥编写,Intel FPGA大学计划经理推荐,骏龙科技大力支持的基于Intel SoC FPGA 器件的设计和开发教程已经上线预售,京东天猫均已上架,欢迎小伙伴围观。

设备树与驱动的关系_9 Linux设备树的原理与应用实例(一)—— 什么是设备树...相关推荐

  1. linux+模块与设备关系,《Linux内核设计与实现》读书笔记 第十七章 设备与模块...

    一.设备类型 1. Unix系统 - 块设备 - 字符设备 - 网络设备 2. 块设备 通常缩写为blkdev,它是可寻址的,寻址以块为单位,块大小随设备不同而不同:块设备通常支持重定位操作,也就是对 ...

  2. 设备树与驱动的关系_Linux CommonClock Framework分析之四 gpio clk gate驱动实现

    前面我们已经完成了CCF子系统的分析,也说明了如何实现CCF驱动,本章为该专栏的最后一篇文章, 本章我们将实现一个虚拟的gpio clk gate驱动.本章大概分为如下几个章节: 一. 本次驱动开发涉 ...

  3. 设备树与驱动的关系_Linux I2C驱动竟然如此简单?手把手教你写i2c驱动

    Linux中I2C驱动框架分析 I2C核心(i2c_core) I2C核心维护了i2c_bus结构体,提供了I2C总线驱动和设备驱动的注册.注销方法,维护了I2C总线的驱动.设备链表,实现了设备.驱动 ...

  4. 设备树与驱动的关系_裸机程序如何驱动硬件?

    任何计算机系统都是软件和硬件的结合体,如果只有硬件而没有软件,则硬件是没有灵魂的躯壳:如果只有软件没有硬件,则软件就是一堆无用的字符.在底层硬件的基础上,操作系统覆盖一层驱动,屏蔽底层硬件的操作,通过 ...

  5. Linux C Socket编程原理及简单实例

    部分转自:http://goodcandle.cnblogs.com/archive/2005/12/10/294652.aspx 1.   什么是TCP/IP.UDP? 2.   Socket在哪里 ...

  6. Linux 文件系统的工作原理深度透析

    磁盘为系统提供了最基本的持久化存储. 文件系统则在磁盘的基础上,提供了一个用来管理文件的树状结构. 那么,磁盘和文件系统是怎么工作的呢?又有哪些指标可以衡量它们的性能呢? 索引节点和目录项 文件系统, ...

  7. Android 驱动(12)---Linux DTS(Device Tree Source)设备树详解

    Linux DTS(Device Tree Source)设备树详解 Linux DTS(Device Tree Source)设备树详解之一(背景基础知识篇) Linux DTS(Device Tr ...

  8. 设备树语法,加载过程和与驱动的关系

    文章目录 一.设备树语法 1.1 简介 1.2 基本数据格式 1.3 一个例子 1.3.1 根节点 2.3.2 CPU 1.3.3 节点名称 1.3.4 设备 1.3.5 status 1.3.6 编 ...

  9. linux 设备树i2驱动,TX2i设备树SPI驱动

    默认/dev下是没有spi设备的@H_301_1@ JetPack版本@H_301_1@ JetPack-L4T-3.2.1-linux-x64_b23.run@H_301_1@ 下载Kernel@H ...

  10. Linux设备驱动程序学习(十六)——Linux设备树解析

    设备树简介   在内核源码中,存在大量对板级细节信息描述的代码.这些代码充斥在/arch/arm/plat-xxx和/arch/arm/mach-xxx目录,对内核而言这些platform设备.res ...

最新文章

  1. 单应性矩阵求解函数findHomography()
  2. golang 短连接和长连接 超时处理
  3. java 名称可以包含-吗_java – “标签属性名称包含无效字符”. “Android Manifest
  4. Linux编程(5)_静态库与动态库
  5. WINDOWS也需要装WINDOWS虚拟机
  6. 网页设计作业作品成品HTML5+CSS大作业——简单的程序员个人博客(7页) 大学个人博客网页制作教程 表格布局网页模板
  7. 关于DNF的多媒体包NPK文件的那些事儿(6) - IMGV4
  8. 拼音四线三格图片_为孩子收藏!小学汉语拼音口诀和书写规则!
  9. 用python爬虫爬取图虫网站图片
  10. 你好 同样在努力的陌生人
  11. 联发科:心态决定未来走势
  12. 金山与永中,谁主沉浮?
  13. Photoshop脚本 合并所有图层
  14. Linux下qt/C++全局键盘监控,处理键盘事件,输出按键码
  15. 如何打造自己有创意的平面设计灵魂作品
  16. 关于mybatis中的大于号和小于号的错误
  17. 灵魂筹码一直显示连接服务器,灵魂筹码进不去怎么办_灵魂筹码进入显示错误解决方法_3DM单机...
  18. ThinkSNS安装指南说明
  19. 六行python代码的爱心曲线公式_六行python代码的爱心曲线-Go语言中文社区
  20. 如何选择适合你的兴趣爱好(六十六),折纸

热门文章

  1. Windows Phone 项目实战之账户助手
  2. 如果你到了20岁,还没到25岁
  3. 4.RabbitMQ 安装
  4. 3.excel 生成 sql
  5. 45. Element isDefaultNamespace() 方法
  6. Windows Server 2008通过计划任务定时执行bat文件
  7. Win10下Pytorch的安装和使用[斗之力三段]
  8. 1.为什么 要需要变量。二进制,方便,2.变量是什么 ,3.声明变量
  9. getElementById和ByTagName的区别
  10. .net将html转换PDF