linux裝置樹的規範(dts和dtb)
第01節_DTS格式
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是記憶體首地址用來區分其它同名的裝置
可以把節點理解為目錄,也就是同一目錄下的子目錄名稱不能相同
有哪些需要注意的事項
比如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
發現修改後暫存器值變了
再次修改
在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檔案佈局如下:
可以看出整個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,存放方式如下:
注意,大端模式和小端模式只針對數值,對於字串 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中節點資訊對應如下:
其中節點資訊結構體如下:
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開發板亮相
瞭解更多