linux裝置樹的規範(dts和dtb)

第01節_DTS格式

dts檔案透過編譯生成dtb格式檔案

linux裝置樹的規範(dts和dtb)

屬性的定義

value取值型別

屬性名=值只有三種取值

第一種 <1 0x3 0x123> (一個或多個32位資料) arrays of cells

第二種 “字串” (用雙引號括起來的值)

第三種 [ 00 11 22] (byte string 是16進製表示的一個或者多個位元組)

一個 byte string必須用2位16進位制數表示 byte之間的空格可以省略,可組合多種型別的值,之間用逗號分開

示例內容

示例:

a。 Arrays of cells : cell就是一個32位的資料interrupts = <17 0xc>;

b。 64bit資料使用2個cell來表示: clock-frequency = <0x00000001 0x00000000>;

c。 A null-terminated string (有結束符的字串): compatible = “simple-bus”;

d。 A bytestring(位元組序列) :local-mac-address = [00 00 12 34 56 78]; 每個byte使用2個16進位制數來表示

e。 可以是各種值的組合, 用逗號隔開:

compatible = “ns16550”, “ns8250”;example = <0xf00f0000 19>, //“a strange property format”;

裝置節點如何定義?

[label:] node-name[@unit-address] { [properties definitions] [child nodes]};

比如

memory@30000000 { device_type = “memory”; reg = <0x30000000 0x4000000>;};

其中memory@30000000就表示node-name[@unit-address]其中的unit-address是記憶體首地址用來區分其它同名的裝置

可以把節點理解為目錄,也就是同一目錄下的子目錄名稱不能相同

linux裝置樹的規範(dts和dtb)

有哪些需要注意的事項

比如2440裝置樹檔案必須要包含的

model = “SMDK2440”; compatible = “samsung,smdk2440”; #address-cells = <1>;//表示子節點的地址寬度是32位 #size-cells = <1>;//表示子節點的位寬是32位

特殊的、預設的屬性:

a。根節點:

#address-cells // 在它的子節點的reg屬性中, 使用多少個u32整數來描述地址(address) #size-cells // 在它的子節點的reg屬性中, 使用多少個u32整數來描述大小(size) compatible // 定義一系列的字串, 用來指定核心中哪個

例如

compatible = “samsung,smdk2440”, “samsung,s3c24xx”; //它會優先去核心中尋找 samsung,smdk2440,如果沒有則尋找samsung,s3c24xx第二項,

*machine_desc可以支援本裝置 // 即這個板子相容哪些平臺 // uImage : smdk2410 smdk2440 mini2440==> machine_desc model // 咱這個板子是什麼 // 比如有2款板子配置基本一致, 它們的compatible是一樣的 // 那麼就透過model來分辨這2款板子

b。 /memory

device_type = “memory”;

reg // 用來指定記憶體的地址、大小

c。 /chosen

bootargs // 核心command line引數, 跟u-boot中設定的bootargs作用一樣

d。 /cpus

/cpus結點下有1個或多個cpu子結點, cpu子結點中用reg屬性用來標明自己是哪一個cpu,

*所以 /cpus 中有以下2個屬性:

#address-cells // 在它的子節點的reg屬性中, 使用多少個u32整數來描述地址(address)

#size-cells // 在它的子節點的reg屬性中, 使用多少個u32整數來描述大小(size) 必須設定為0

e。 /cpus/cpu*

device_type = “cpu”;

reg // 表明自己是哪一個cpu

引用其他節點:

a。 phandle : // 節點中的phandle屬性, 它的取值必須是唯一的(不要跟其他的phandle值一樣)

pic@10000000 { phandle = <1>; interrupt-controller;};another-device-node { interrupt-parent = <1>; // 使用phandle值為1來引用上述節點};

b。 label:

PIC: pic@10000000 { interrupt-controller;};another-device-node { interrupt-parent = <&PIC>; // 使用label來引用上述節點, // 使用lable時實際上也是使用phandle來引用, // 在編譯dts檔案為dtb檔案時, 編譯器dtc會在dtb中插入phandle屬性};

舉例說明

如果我想在dts中包含dtsi檔案

新建 jz2440。dtsi複製jz2440。dtsdtsi檔案時dts的父節點可以直接引用,語法格式相同,在dts檔案中引用dtsi,比如想修改某個引腳,但是又不想修改dtsi檔案,則只需要在dts檔案中覆蓋掉原來的的配置即可

#include “jz2440。dtsi”/{ led { ping = ; } }

上傳檔案,

設定環境變數,編譯

如果我想反編譯dtb檔案怎麼做?

當前目錄下執行

。/scripts/dtc/dtc -I 輸入檔案dtb -O 輸出檔案dts -o tmp。dts(輸出檔名) 指定dtb檔案所在位置

。/scripts/dtc/dtc -I dtb -O dts -o tmp。dts arch/arm/boot/dts/jz2440。dtb

linux裝置樹的規範(dts和dtb)

發現修改後暫存器值變了

再次修改

在dtsi中的led節點上新增lable

LED:led { compatible = “jz2440_led”; pin = ;};

在dts檔案中覆蓋

&LED{ pin = ;};

上傳檔案,

設定環境變數,編譯,反編譯dtb檢視已經變化

還可以檢視核心目錄\linux-4。19-rc3\Documentation\devicetree\usage-model。txt檔案

Linux uses DT data for three major purposes:

platform identification,

runtime configuration, and

device population。

比如你想保留某塊記憶體,保留記憶體的起始地址以及大小

/memreserve/ 0x33000000 0x10000

這些配置屬於runtime configuration

比如led就屬於device population。

第02節_DTB格式

這節影片開始講解裝置樹的DTB格式。

DTS變成DTB

在dtsi檔案裡,我們使用了各種C語言類似的宏,這些宏需要在被使用的地方展開;

dtsi和dts檔案中,都是可讀性非常強的程式碼,容易引入錯誤,需要檢測這些錯誤;

在dts檔案裡,可以包含一個或多個dtsi檔案,這就意味著原始檔有很多,需要將它們編譯成一個唯一的檔案;

dtsi和dts檔案中,後面屬性的值要覆蓋前面同名的屬性的值;

使用dtc工具將dtsi和dts變成dtb檔案時,該工具就自動完成前面的四個操作。

DTB檔案佈局

DTB檔案佈局如下:

linux裝置樹的規範(dts和dtb)

可以看出整個DTB分為四個部分:struct ftd_header、memory reservation block、structure block、strings block;

struct ftd_header:用來表明各個分部的偏移地址,整個檔案的大小,版本號等;

memory reservation block:在裝置樹中使用/memreserve/ 定義的保留記憶體資訊;

structure block:儲存節點的資訊,節點的結構;

strings block:儲存屬性的名字,單獨作為字串儲存;

使用命令make dts編譯JZ2440的裝置樹檔案,生成DTB檔案,再使用UltraEdit工具開啟,方便檢視16進位制,進行分析dts和dtb的對應關係。

struct ftd_header結構體的定義如下:

struct fdt_header {uint32_t magic;uint32_t totalsize;uint32_t off_dt_struct;uint32_t off_dt_strings;uint32_t off_mem_rsvmap;uint32_t version;uint32_t last_comp_version;uint32_t boot_cpuid_phys;uint32_t size_dt_strings;uint32_t size_dt_struct;};

在DTB檔案中,資料的存放格式是大端模式,即數值的高位存放在低地址。

補充知識:大端(big endian)小端(little endian)

對於一個值,比如0x12345678,存放方式如下:

linux裝置樹的規範(dts和dtb)

注意,大端模式和小端模式只針對數值,對於字串 abc,a在低地址,c在高地址。

分析DTB內容

下面開始分析DTB的內容:

首先是ftd_header結構體中的magic,為0xd00dfeed;

然後是totalsize,整個DTB檔案的大小;

再是off_dt_struct,即structure block的偏移地址;

再是off_dt_strings,即strings block的偏移地址;

再是off_mem_rsvmap,即memory reservation block的偏移地址;

因此,根據偏移,就能找到DTB每個部分的內容。

structure block儲存節點的資訊,節點的結構,和DTS中節點資訊對應如下:

linux裝置樹的規範(dts和dtb)

其中節點資訊結構體如下:

struct { uint32_t len; uint32_t nameoff;}

len表示val長度;

nameoff表示在string block的偏移;

總結

最後總結一下:

DTB檔案可以分為四個部分:struct ftd_header、memory reservation block、structure block、strings block;

最開始的為struct ftd_header,包含其它三個部分的偏移地址;

memory reservation block記錄保留記憶體資訊;

structure block儲存節點的資訊,節點的結構;

strings block儲存屬性的名字,將屬性名字單獨作為字串儲存;

您可能對以下內容感興趣:

預熱 | 萬眾期待的微控制器,Linux二合一的STM32MP157開發板亮相

瞭解更多