NCS-OS系列3 :devicetree介紹
文章目錄
- NCS-OS系列3 :devicetree介紹
- 前言
- 一、devicetree概念
- 二、文法和結構
- 三、Unit address示例
-
- 3.1 記憶體映射外設
- 3.2 I2C外設
- 3.3 SPI外設
- 3.4 存儲器
- 3.5 記憶體映射flash
- 3.6 記憶體分區
- 四、重要屬性介紹
-
- 4.1 compatible
- 4.2 label
- 4.3 reg
- 4.4 status
- 4.5 interrupts
- 五、Aliases and chosen nodes
- 六、輸入輸出檔案
-
- 6.1 輸入檔案
- 6.2 腳本和工具
- 6.3 輸出檔案
- 6.4 寫屬性值
前言
ncs 相關文章,部分為原始文檔翻譯,水準有限,如果有錯誤,歡迎指出。
本文參考連結:
https://docs.zephyrproject.org/latest/guides/dts/intro.html
一、devicetree概念
devicetree
是描述硬體的階層化的資料結構。Devicetree specification定義了它的源代碼和二進制表示。Zephyr使用
devicetree
來描述其支援的單闆上可用的硬體,以及該硬體的初始配置。
有兩種類型的
devicetree
輸入檔案:
devicetree sources
和
devicetree bindings
。
devicetree sources
包含
devicetree
本身。
devicetree bindings
描述它的内容,包括資料類型。編譯系統使用
devicetree sources
和
devicetree bindings
來建立一個生成的C頭檔案。生成的頭檔案的内容被
devicetree.h
API分離出來,可以使用該API從
devicetree
中擷取資訊。
下面是一個簡單的示例圖:

所有Zephyr和應用程式的源代碼檔案都可以include并使用
devicetree.h
。這包括裝置驅動程式、應用程式、測試、核心等。
API本身是基于C宏定義的。宏名都以
DT_
開頭。通常,在Zephyr源檔案中看到一個以
DT_
開頭的宏,那麼它可能是一個
devicetree.h
宏。生成的C頭檔案也包含以
DT_
開頭的宏,這些可能會在編譯器的錯誤資訊中看到。生成的宏和非生成的宏可以通過這種方式來區分:生成的宏有一些小寫字母,而
devicetree.h
宏的名字都是大寫字母。
二、文法和結構
如名字所示,
devicetree
是一個樹型結構,這個樹的人類可讀的文本格式稱為DTS(用于
devicetree source
),并在Devicetree specification中定義。
下面是一個示例的DTS檔案:
/dts-v1/;
/ {
a-node {
subnode_label: a-sub-node {
foo = <3>;
};
};
};
/ dts-v1 /;
表示檔案的内容是使用DTS文法的版本1,它取代了現在已經過時的“版本0”。
這個樹有三個節點:
- 1個根節點:
/
- 一個根節點的子節點,名字為:
a-node
- 一個
的子節點,名字為:a-node
節點可以a-sub-node
節點可以被賦予
labels
,标簽是用來在裝置樹的其他地方引用标簽節點的唯一的簡寫。上面的DTS檔案中,
a-sub-node
的
label
就是
subnode_label
。一個節點可以有0、1或多個節點
label
。
Devicetree節點具有辨別其在樹中的位置的路徑。與Unix檔案系統路徑一樣,Devicetree路徑是由斜杠(
/
)分隔的字元串,根節點的路徑是單個斜杠:
/
。每個節點的路徑将由節點祖先節點的名稱與節點自己的名稱(用斜杠分隔)連接配接而成。例如,到
a-sub-node
的全路徑為
“/a-node/a-sub-node”
。
節點
a-sub-node
有一個名為
foo
的屬性,它的值是一個值為
3
的單元。
foo
值的大小和類型由DTS中的尖括号(
<和>
)包含。有關更多屬性值的示例,請參閱下面的 Writing property values 部分。
在實際應用中,
devicetree
節點通常對應于某些硬體,節點的層次結構反映了硬體的實體布局。假設一塊闆,它有三個I2C外設連接配接到一個SoC上的I2C總線控制器上,如下所示:
對應的I2C總線控制器和每個I2C外圍裝置的節點将出現在
devicetree
中。反映硬體的布局,I2C外設節點将是總線控制器節點的子節點。類似的規則也适用于表示其他類型的硬體。
上面的DTS檔案将會如下所示:
/dts-v1/;
/ {
soc {
i2c-bus-controller {
i2c-peripheral-1 {
};
i2c-peripheral-2 {
};
i2c-peripheral-3 {
};
};
};
};
屬性在實際應用中用于描述或配置節點表示的硬體。例如,I2C外設節點具有一個屬性,其值是總線上的外設位址。
下面是一個和上面功能一樣的樹,但其中包含使用I2C裝置時可能看到的真實節點名稱和屬性:
上面的
devicetree
轉換成DTS檔案如下所示:
/dts-v1/;
/ {
soc {
i2c@40003000 {
compatible = "nordic,nrf-twim";
label = "I2C_0";
reg = <0x40003000 0x1000>;
apds9960@39 {
compatible = "avago,apds9960";
label = "APDS9960";
reg = <0x39>;
};
ti_hdc@43 {
compatible = "ti,hdc", "ti,hdc1010";
label = "HDC1010";
reg = <0x43>;
};
mma8652fc@1d {
compatible = "nxp,fxos8700", "nxp,mma8652fc";
label = "MMA8652FC";
reg = <0x1d>;
};
};
};
};
除了顯示更真實的名稱和屬性外,上面的示例還引入了一個新的
devicetree
概念:
unit addresses
。
unit addresses
是在“at”符号(
@
)之後的節點名的部分,如
[email protected]
中的
40003000
,或
[email protected]
中的
39
。
unit addresses
是可選的:soc節點沒有
unit addresses
。
三、Unit address示例
在
devicetree
中,
Unit address
給出了在其父節點的位址空間中的節點位址。下面是一些不同類型硬體的
Unit address
示例
3.1 記憶體映射外設
外設的寄存器映射基位址。例如,名稱為
[email protected]
的節點代表一個寄存器映射基地位址為
0x40003000
的I2C控制器。
3.2 I2C外設
I2C總線上的外設位址。例如,上一節中I2C控制器的子節點
[email protected]
的I2C位址是
0x39
。
3.3 SPI外設
代表外圍裝置的晶片
CS線
的索引。(如果沒有晶片
CS線
,則使用0。)
3.4 存儲器
實體起始位址。例如,一個名為
[email protected]
的節點表示從實體位址
0x2000000
開始的RAM。
3.5 記憶體映射flash
像RAM一樣,實體起始位址。例如,名稱為
[email protected]
的節點代表實體起始位址為
0x8000000
的flash裝置。
3.6 記憶體分區
這适用于
devicetree
用于存儲flash分區表的時候。
unit address
是
flash
中分區的開始偏移量。例如,以這個閃存裝置及其分區為例:
flash@8000000 {
/* ... */
partitions {
partition@0 { /* ... */ };
partition@20000 { /* ... */ };
/* ... */
};
};
名為
[email protected]
的節點從其flash裝置的開始處偏移量為0,是以其基址為
0x8000000
。類似地,名為
[email protected]
的節點的基址是
0x8020000
。
四、重要屬性介紹
4.1 compatible
compatible
是節點所代表的硬體裝置的名稱,推薦的格式是
"vendor,device"
,如
“avago,apds9960”
,或這些的序列,如
“ti,hdc”
,
“ti,hdc1010”
。vendor部分是廠商名稱的縮寫。檔案
dts/bindings/vendor-prefixes.txt
中包含了一個通用的的供應商名稱清單。device部分通常取自裝置的datasheet。
當硬體的表現是通用的時,它也可以是像
gpio-keys
、
mmio-sram
或
fixed-clock
這樣的值。
編譯系統使用
compatible
屬性為節點找到正确的
bindings
。裝置驅動程式使用
devicetree.h
來查找具有相關
compatibles
的節點,以便确定要管理的可用硬體。
compatible
屬性可以有多個值。當裝置是一個通用系列的特定執行個體時,會使用附加值以允許系統從最特定的裝置驅動程式到最不特定的裝置驅動程式比對。
在Zephyr的綁定文法中,此屬性的類型為
string-array
。
4.2 label
label
是根據Zephyr的裝置驅動程式模型實作的裝置的名稱。這個值可以傳遞給
device_get_binding()
來檢索相應的驅動程式級結構
device*
。這個指針可以通過應用程式代碼傳遞給正确的裝置驅動程式API來與裝置互動。例如,調用
device_get_binding("I2C_0")
将傳回一個指向裝置結構的指針,該結構可以傳遞給
I2C API
函數,如
i2c_transfer()
。生成的C頭檔案同樣會包含一個可以描述這個字元串的宏。
4.3 reg
reg
是用于給裝置尋址的資訊。該值是給到特定裝置的(即根據
compatible
屬性不同而不同)。
reg
屬性是一個由
(address, length)
對組成的序列。每一對被稱為一個
“register block”
。下面是一些常見的模式:
- 通過記憶體映射I/O寄存器通路的裝置(如
):[email protected]
通常是I/O寄存器空間的基址,而address
是寄存器所占用的位元組數。length
- I2C裝置(如
及其系列):[email protected]
是I2C總線上的從位址。沒有address
值。length
- SPI裝置:
是晶片CS線,沒有address
上面描述的length
屬性和reg
之間有一些相似之處,unit addresses
屬性可以被看作是裝置中比reg
更詳細的可尋址資源。unit addresses
4.4 status
status
是描述節點是否啟用的字元串,devicetree規範允許該屬性的值可以為
“okay”
、
“disabled”
、
“reserved”
、
“fail”
和
“fail-sss”
。目前隻有
“okay”
和
“disabled”
值與Zephyr相關,使用其他值會導緻未定義的行為。
如果一個節點的
status
屬性是
“okay”
或者沒有定義(即在
devicetree source
中不存在),那麼它就被認為是
enabled
。狀态為
“disabled”
的節點被顯式禁用。(為了向後相容,值
“ok”
被視為與
“okay”
相同,但這種用法已棄用),在配置設定和初始化Zephyr驅動模型中的相應
struct device
時,必須啟用與實體裝置對應的Devicetree節點。
4.5 interrupts
interrupts
是由裝置産生的中斷的資訊,編碼為一個或多個中斷訓示符的數組。每個中斷訓示符都有一定數量的單元。更多細節請參見 Devicetree Specification release v0.3中section 2.4, Interrupts and Interrupt Mapping章節。
五、Aliases and chosen nodes
除了
node label
之外,還有另外兩種方法可以引用特定的節點,而無需指定其整個路徑: 通過
alias
或
chosen node
。
下面的devicetree使用了這兩個概念:
/dts-v1/;
/ {
chosen {
zephyr,console = &uart0;
};
aliases {
my-uart = &uart0;
};
soc {
uart0: serial@12340000 {
...
};
};
};
/alias
和
/chosen
節點并不指向實際的硬體裝置,它們的目的是指定
devicetree
中的其他節點。
上面的dts檔案中,
my_uart
是路徑為
/soc/[email protected]
的裝置的alias,通過使用它的
node label
值
uart0
,
zephyr,console
這個
chosen node
被設定為同樣的值。
Zephyr示例應用程式有時使用别名以允許以通用方式來取代應用程式使用的特定硬體裝置。例如,Blinky例程使用這個來抽象LED通過
led0 alias
閃爍。
/chosen
節點屬性用于配置系統或子系統範圍的值。更多資訊,請參閱Chosen nodes。
六、輸入輸出檔案
本節更詳細地描述了本介紹開頭的圖中所示的輸入和輸出檔案。
6.1 輸入檔案
有四種類型的devicetree輸入檔案:
- sources (.dts)
- includes (.dtsi)
- overlays (.overlay)
- bindings (.yaml)
ncs目錄中的裝置樹檔案是這樣的:
ncs/zephyr/boards/<ARCH>/<BOARD>/<BOARD>.dts
ncs/zephyr/dts/common/skeleton.dtsi
ncs/zephyr/dts/<ARCH>/.../<SOC>.dtsi
ncs/zephyr/dts/bindings/.../binding.yaml
還有:
ncs/nrf/boards/<ARCH>/<BOARD>/<BOARD>.dts
ncs/nrf/dts/bindings/.../binding.yaml
一般來說,每一個支援的
BOARD
,都要有一個對應的
BOARD.dts
檔案來描述它的硬體結構,例如
reel_board
的dts檔案為
boards/arm/reel_board/reel_board.dts
BOARD.dts
包括一個或多個
.dtsi
檔案,這些
.dtsi
檔案或者通過
include
其他
.dtsi
檔案來描述運作Zephyr的CPU或片上系統。它們還可以描述由多個闆共享的其他常見硬體特征。除了這些,
BOARD.dts
也描述了闆子的具體硬體。
dts/common
目錄包含
skeleton.dtsi
,
skeleton.dtsi
是用于定義完整
devicetree
的最小包含檔案。特定體系結構的子目錄(
dts/<ARCH>
)包含了針對
CPUs
或
SoCs
的對
skeleton.dtsi
擴充的
.dtsi
檔案。
C預處理器在所有
devicetree
檔案上運作以展開宏引用,而
include
通常使用
#include <filename>
指令來完成,即使DTS有
/include/ "<filename>"
文法。
BOARD.dts
可以使用
overlays
檔案進行擴充或修改。Overlay也是DTS檔案,
overlay
擴充隻是一個是目的更明确的慣例。Overlays調整基礎的
devicetree
以适應不同的目的:
- Zephyr應用程式可以使用
來啟用預設禁用的外圍裝置,為特定的應用程式選擇闆上的傳感器,等等。通過使用overlay
,可以重新配置核心和裝置驅動程式,而無需修改源代碼。Kconfig
- 在定義
時也使用Shields
Overlays
。
編譯系統自動擷取存儲在特定位置的
檔案。也可以通過.overlay
這個CMake變量顯式列出要包含的DTC_OVERLAY_FILE
。詳情參見Set devicetree overlay。overlays
編譯系統将
BOARD.dts
和其他
.overlay
檔案進行合并,
overlay
放在最後,這依賴于DTS允許合并
devicetree
中節點的重複定義的文法。見示例:FRDM-K64F and Hexiwear K64,這是一個在
.dtsi
檔案的環境中如何工作的示例,但原理與
overlays
相同,把
.overlay
檔案的内容放在最後允許它們覆寫
BOARD.dts
。
Devicetree bindings(
YAML檔案
)本質上是
glue
。它們以一種允許編譯系統生成裝置驅動程式和應用程式可用的C宏的方式來描述
devicetree sources
,
includes
, 和
overlays
的内容。
dts/bindings
目錄包含了相關的
bindings
資訊。
Zephyr目前使用
dts_fixup.h
檔案将
devicetree_unfixed.h
中的宏重命名為C代碼目前使用的名稱。預設情況下,建構系統在
zephyr/boards/
和
zephyr/soc/
目錄中查找修複檔案。修複檔案的存在是由于曆史原因,新代碼通常應該避免使用它們。
6.2 腳本和工具
下面的庫和腳本位于
scripts/dts/
中,它們使用輸入檔案來建立輸出檔案。
dtlib.py : 底層DTS解析庫
edtlib.py : 位于dtlib之上的庫,它使用
bindings
來解析屬性,并提供
devicetree
的更高層次視圖,使用dtlib進行DTS解析。
gen_defines.py :使用edtlib從
devicetree
和
bindings
生成C預處理器宏的腳本
另外,如果在您的系統上安裝了标準的dtc(
devicetree compiler
)工具,那麼它将在最終的
devicetree
上運作,這是為了捕獲錯誤或警告。輸出資訊沒有被使用。闆子可能需要傳遞dtc附加标志,例如
warning suppression
。單闆目錄中可以包含一個名為
pre_dt_board.cmake
的檔案,它配置了這些額外的标志,像這樣:
6.3 輸出檔案
下面這些是在應用程式編譯檔案夾中建立的
<build>/zephyr/include/generated/devicetree_unfixed.h
生成的宏和描述
devicetree
的附加注釋,通過
devicetree.h
進行
include
。
<build>/zephyr/include/generated/devicetree_fixups.h
dts_fix.h
檔案的内容,通過
devicetree.h
進行
include
。
<build>/zephyr/zephyr.dts
最終合并後的
devicetree
。這個檔案由
gen_definitions .py
輸出,作為調試目的使用,沒有其他用處。
<build>/zephyr/<BOARD>.dts.pre.tmp
預處理和連接配接的DTS
sources
和
overlays
。這是一個中間輸出檔案,用于建立
zephyr.dts
和
devicetree_unfixed.h
。
6.4 寫屬性值
下面是一些以DTS格式寫入屬性值的示例方法。為了盡量簡單,省略了一些細節;如果對細節感興趣,請參閱devicetree specification。
32位無符号整數數組或
cells
,可以寫在尖括号(
<和>
)之間,并用空格分隔:
foo屬性值有三個
cells
,值依次為
0xdeadbeef
、
1234
和
。注意,這裡允許使用十六進制數和十進制數,并且可以混合使用。由于Zephyr将DTS轉換為C源檔案,是以沒有必要在這裡指定單個cell的位元組順序。
64位整數按大端順序寫成兩個32位單元格。值
0xaaaa0000bbbb1111
寫為
<0xaaaa0000 0xbbbb1111>
。
允許使用圓括号、算術操作符和位操作符。下面
bar
屬性包含一個值為64的單元格:
字元串用雙引号括起來:
字元串數組用逗号分隔:
位元組數組以十六進制形式寫入,方括号(
[和]
)之間不包含前導0x。下面的屬性
a-byte-array
是三個位元組0x00, 0x01, 0xab:
屬性可以通過它們的
phandles
引用
devicetree
中的其他節點。可以使用
&label
寫一個
phandle
,就像在這個
devicetree
片段中:
baz: device@0 {
/* ... */
};
device@1 {
sibling = <&baz 1 2>;
/* ... */
};
節點
[email protected]
的
sibling
屬性包含三個單元格:
-
節點的[email protected]
。每個phandle
占據1個cell。phandle
标簽用于在baz
屬性中寫入phandle值sibling
。&baz
- 值1和2,每個都按照這個順序在自己的單元格中。