Skip to content

Commit 0d1fa6d

Browse files
committed
[TOOLS] Add DTC (Devicetree Compiler) tools
Signed-off-by: GuEe-GUI <[email protected]>
1 parent 593ac8d commit 0d1fa6d

File tree

4 files changed

+328
-0
lines changed

4 files changed

+328
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
*.ilk
1212
*.old
1313
*.crf
14+
*.dtb*
1415
build
1516
Debug
1617
.vs

documentation/6.components/device-driver/INDEX.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@
1313
- @subpage page_device_wlan
1414
- @subpage page_device_sensor
1515
- @subpage page_device_audio
16+
- @subpage page_device_dtc
Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
@page page_device_dtc Devicetree Compiler
2+
3+
# Introduction to the DTC
4+
5+
Device Tree Compiler, dtc, takes as input a device-tree in a given format and outputs a device-tree in another format for booting kernels on embedded systems.
6+
Typically, the input format is "dts" (device-tree source), a human readable source format, and creates a "dtb" (device-tree binary), or binary format as output.
7+
8+
> If the dtc tool is not installed on your host system, the dtc module will guide you through the installation.
9+
10+
## Generate DTS
11+
12+
When you have a DTB or FDT file from firmware or another runtime system, you might want to convert it into a DTS file for easier reading.
13+
You can do this in Python or your SConscript file. For example, assuming you have `dummpy.dtb`:
14+
15+
```python
16+
import os, sys
17+
18+
RTT_ROOT = os.getenv('RTT_ROOT')
19+
sys.path.append(RTT_ROOT + '/tools')
20+
21+
from building import *
22+
import dtc
23+
24+
dtc.dtb_to_dts(RTT_ROOT, "dummpy.dtb")
25+
```
26+
27+
This will generate a dummpy.dts in the current directory. If a file with the same name already exists, it will be replaced.
28+
To avoid overwriting, you can specify a different output name:
29+
30+
```python
31+
[...]
32+
33+
dtc.dtb_to_dts(RTT_ROOT, "dummpy.dtb", "dummpy-tmp.dts")
34+
# or
35+
dtc.dtb_to_dts(RTT_ROOT, "dummpy.dtb", dts_name = "dummpy-tmp.dts")
36+
```
37+
38+
## Generate DTB
39+
40+
Before generating a DTB, you may want to review the basics of DTS syntax and structure: [DeviceTree Specification](https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html)
41+
42+
### Include and Macros
43+
44+
By default, dtc does not support C-style preprocessing (like cpp), but you can use the C preprocessor with your DTS files.
45+
Don't worry — our dtc module already includes this step.
46+
47+
If your DTS file uses dt-bindings headers or macros, you can write something like:
48+
49+
```c
50+
/*
51+
* Used "#include" if header file need preprocessor,
52+
* `components/drivers/include` and current directory path is default.
53+
*/
54+
#include <dt-bindings/size.h>
55+
#include "dummy.dtsi"
56+
/* Well, if dtsi is simple, you can use "/include/", it is supported by dtc */
57+
/include/ "chosen.dtsi"
58+
59+
#define MMIO_BASE 0x10000
60+
#define MMIO_SIZE SIZE_GB
61+
#define MEM_BASE (MMIO_BASE + MMIO_SIZE)
62+
63+
#ifndef CPU_HARDID
64+
#define CPU_HARDID 0
65+
#endif
66+
67+
#ifndef SOC_INTC
68+
#define SOC_INTC intc_a
69+
#endif
70+
71+
/ {
72+
#address-cells = <2>;
73+
#size-cells = <2>;
74+
/*
75+
* Macros after "&" will be replaced,
76+
* there will affect the interrupt controller in this SoC.
77+
*/
78+
interrupt-parent = <&SOC_INTC>;
79+
80+
[...]
81+
82+
memory {
83+
/* When there is a calculation, please use "()" to include them */
84+
reg = <0x0 MEM_BASE 0x0 (3 * SIZE_GB)>;
85+
device_type = "memory";
86+
};
87+
88+
cpus {
89+
#size-cells = <0>;
90+
#address-cells = <1>;
91+
92+
/* Macros after "@" will be replaced */
93+
cpu0: cpu@CPU_HARDID {
94+
reg = <CPU_HARDID>;
95+
device_type = "cpu";
96+
};
97+
};
98+
99+
/* Macros replace support phandle name, too */
100+
intc_a: intc-a {
101+
interrupt-controller;
102+
};
103+
104+
intc_b: intc-b {
105+
interrupt-controller;
106+
};
107+
108+
[...]
109+
};
110+
```
111+
112+
To generate the DTB:
113+
114+
```python
115+
import os, sys
116+
117+
RTT_ROOT = os.getenv('RTT_ROOT')
118+
sys.path.append(RTT_ROOT + '/tools')
119+
120+
from building import *
121+
import dtc
122+
123+
dtc.dts_to_dtb(RTT_ROOT, ["dummpy.dts"]
124+
```
125+
126+
To append more include paths, for example, SoC DM headers:
127+
128+
```python
129+
[...]
130+
131+
dtc.dts_to_dtb(RTT_ROOT, ["dummpy.dts"], include_paths = ['dm/include', 'firmware'])
132+
```
133+
134+
### Multiple DTB
135+
136+
A single SoC may have different board variants.
137+
Example `dummy.dtsi` (common base):
138+
139+
```c
140+
/* SoC dummy */
141+
/ {
142+
#address-cells = <2>;
143+
#size-cells = <2>;
144+
model = "Dummy SoC Board";
145+
146+
[...]
147+
148+
chosen {
149+
bootargs = "cma=8M coherent_pool=2M";
150+
};
151+
152+
reserved-memory {
153+
#address-cells = <2>;
154+
#size-cells = <2>;
155+
156+
isp_shm@100000 {
157+
reg = <0x0 0x100000 0x0 0x100000>;
158+
};
159+
160+
dsp_shm@200000 {
161+
reg = <0x0 0x200000 0x0 0x100000>;
162+
};
163+
};
164+
165+
dsp {
166+
status = "okay";
167+
};
168+
169+
buddy {
170+
isp = <&{/reserved-memory/isp_shm@100000}>;
171+
dsp = <&{/reserved-memory/dsp_shm@200000}>;
172+
};
173+
174+
uart0: uart {
175+
status = "disabled";
176+
};
177+
178+
i2c0: i2c {
179+
status = "okay";
180+
};
181+
182+
[...]
183+
};
184+
```
185+
186+
For a vendor-specific variant (Vendor A):
187+
188+
```c
189+
/* vendorA dummy */
190+
#include "dummy.dtsi"
191+
192+
/ {
193+
/* No phandle name can modify in place */
194+
chosen {
195+
bootargs = "console=uart0 cma=8M coherent_pool=2M";
196+
};
197+
};
198+
199+
/* Reference and modify direct if has phandle name */
200+
&uart0 {
201+
status = "okay";
202+
pinctrl-0 = <&uart0_m1>;
203+
};
204+
205+
&i2c0 {
206+
status = "disabled";
207+
};
208+
```
209+
210+
To remove nodes or properties (Vendor B):
211+
212+
```c
213+
/* vendorB dummy */
214+
#include "dummy.dtsi"
215+
216+
/delete-node/ &dsp_shm;
217+
218+
/ {
219+
/* Delete in place if no phandle name */
220+
/delete-node/ dsp;
221+
222+
/* Delete property */
223+
buddy {
224+
/delete-property/ dsp;
225+
};
226+
};
227+
```
228+
229+
To add new devices (Vendor C):
230+
231+
```c
232+
/* vendorC dummy */
233+
#include "dummy.dtsi"
234+
235+
&i2c0 {
236+
rtc@0 {
237+
clock-frequency = <32768>;
238+
};
239+
};
240+
```
241+
242+
Build all DTBs together:
243+
244+
```python
245+
[...]
246+
247+
dtc.dts_to_dtb(RTT_ROOT, ["dummpy-vendorA.dts", "dummpy-vendorB.dts", "dummpy-vendorC.dts"])
248+
```
249+
250+
This will produce `dummpy-vendorA.dtb`, `dummpy-vendorB.dtb`, and `dummpy-vendorC.dtb`
251+
252+
## Warnings
253+
254+
DTC may produce warnings that are irrelevant or noisy.
255+
To suppress specific warnings:
256+
257+
```python
258+
[...]
259+
260+
dtc.dts_to_dtb(RTT_ROOT, ["dummpy.dts"], ignore_warning = ["simple_bus_reg", "unit_address_vs_reg", "clocks_is_cell", "gpios_property"])
261+
```
262+
263+
Make sure your DTS is valid!
264+
265+
## Raw options
266+
267+
DTC provides additional command-line options (see dtc --help). You can pass raw options like this:
268+
269+
```python
270+
[...]
271+
272+
dtc.dtb_to_dts(RTT_ROOT, "dummpy.dtb", options = "--quiet")
273+
dtc.dts_to_dtb(RTT_ROOT, ["dummpy.dts"], options = "--quiet")
274+
```

tools/dtc.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
#
2+
# Copyright (c) 2006-2023, RT-Thread Development Team
3+
#
4+
# SPDX-License-Identifier: Apache-2.0
5+
#
6+
# Change Logs:
7+
# Date Author Notes
8+
# 2023-05-10 GuEe-GUI the first version
9+
#
10+
11+
import os, re
12+
13+
from building import *
14+
15+
__dtc_install_tip = """
16+
You should install dtc (devicetree compiler) in your system:
17+
Linux:
18+
Debian/Ubuntu: apt-get install device-tree-compiler
19+
Arch/Manjaro: pacman -Sy dtc
20+
21+
MacOS:
22+
brew install dtc
23+
24+
Windows (MinGW):
25+
msys2: pacman -S dtc
26+
"""
27+
28+
def __check_dtc(value):
29+
if value != 0 and os.system("dtc -v") != 0:
30+
print(__dtc_install_tip)
31+
32+
def dts_to_dtb(RTT_ROOT, dts_list, options = "", include_paths = [], ignore_warning = []):
33+
path = GetCurrentDir() + '/'
34+
warning_ops = ""
35+
for warning in ignore_warning:
36+
warning_ops += " -W no-" + warning
37+
for dts in dts_list:
38+
dtb = dts.replace('.dts', '.dtb')
39+
if not os.path.exists(path + dtb) or os.path.getmtime(path + dtb) < os.path.getmtime(path + dts):
40+
tmp_dts = dts + '.tmp'
41+
Preprocessing(dts, None, output = tmp_dts, CPPPATH=[RTT_ROOT + '/components/drivers/include'] + include_paths)
42+
ret = os.system("dtc -I dts -O dtb -@ -A {} {} {} -o {}".format(warning_ops, options, path + tmp_dts, path + dtb))
43+
__check_dtc(ret)
44+
if os.path.exists(path + tmp_dts):
45+
os.remove(path + tmp_dts)
46+
47+
def dtb_to_dts(RTT_ROOT, dtb_name, dts_name = None, options = ""):
48+
path = GetCurrentDir() + '/'
49+
if dts_name == None:
50+
dts_name = re.sub(r'\.dtb[o]*$', '.dts', dtb_name)
51+
ret = os.system("dtc -I dtb -O dts {} {} -o {}".format(options, path + dtb_name, path + dts_name))
52+
__check_dtc(ret)

0 commit comments

Comments
 (0)