Skip to content

Commit dc0a625

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

File tree

3 files changed

+323
-0
lines changed

3 files changed

+323
-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
Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
# 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 you not install dtc tools in your host system, the dtc module will tell you how to do.
9+
10+
### Generate DTS
11+
12+
When you get a DTB or FDT file from firmware or the other runtime system, you may want to convert it to dts for reading easier. Just do in your Python or SConscript, dummpy.dtb, eg.:
13+
14+
```python
15+
import os, sys
16+
17+
RTT_ROOT = os.getenv('RTT_ROOT')
18+
sys.path.append(RTT_ROOT + '/tools')
19+
20+
from building import *
21+
import dtc
22+
23+
dtc.dtb_to_dts(RTT_ROOT, "dummpy.dtb")
24+
```
25+
26+
Then you will get a `dummpy.dts` in current directory, dts file will replace if exists before, so you can try another name like this:
27+
28+
```python
29+
[...]
30+
31+
dtc.dtb_to_dts(RTT_ROOT, "dummpy.dtb", "dummpy-tmp.dts")
32+
# or
33+
dtc.dtb_to_dts(RTT_ROOT, "dummpy.dtb", dts_name = "dummpy-tmp.dts")
34+
```
35+
36+
### Generate DTB
37+
38+
First, you may want to know some knowledge about dts: [DeviceTree Specification](https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html)
39+
40+
#### Include and Macros
41+
42+
Gog, dtc is not supports preprocess like C preprocessor (cpp), but you can use cpp to do it in the dts, don't worry, we have appended the step in dtc module.
43+
44+
When you have some `dt-bindings` include files or macros, like this:
45+
46+
```c
47+
/*
48+
* Used "#include" if header file need preprocessor,
49+
* `components/drivers/include` and current directory path is default.
50+
*/
51+
#include <dt-bindings/size.h>
52+
#include "dummy.dtsi"
53+
/* Well, if dtsi is simple, you can use "/include/", it is supported by dtc */
54+
/include/ "chosen.dtsi"
55+
56+
#define MMIO_BASE 0x10000
57+
#define MMIO_SIZE SIZE_GB
58+
#define MEM_BASE (MMIO_BASE + MMIO_SIZE)
59+
60+
#ifndef CPU_HARDID
61+
#define CPU_HARDID 0
62+
#endif
63+
64+
#ifndef SOC_INTC
65+
#define SOC_INTC intc_a
66+
#endif
67+
68+
/ {
69+
#address-cells = <2>;
70+
#size-cells = <2>;
71+
/*
72+
* Macros after "&" will be replaced,
73+
* there will affect the interrupt controller in this SoC.
74+
*/
75+
interrupt-parent = <&SOC_INTC>;
76+
77+
[...]
78+
79+
memory {
80+
/* When there is a calculation, please use "()" to include them */
81+
reg = <0x0 MEM_BASE 0x0 (3 * SIZE_GB)>;
82+
device_type = "memory";
83+
};
84+
85+
cpus {
86+
#size-cells = <0>;
87+
#address-cells = <1>;
88+
89+
/* Macros after "@" will be replaced */
90+
cpu0: cpu@CPU_HARDID {
91+
reg = <CPU_HARDID>;
92+
device_type = "cpu";
93+
};
94+
};
95+
96+
/* Macros replace support phandle name, too */
97+
intc_a: intc-a {
98+
interrupt-controller;
99+
};
100+
101+
intc_b: intc-b {
102+
interrupt-controller;
103+
};
104+
105+
[...]
106+
};
107+
```
108+
109+
Generate DTS, now:
110+
111+
```python
112+
import os, sys
113+
114+
RTT_ROOT = os.getenv('RTT_ROOT')
115+
sys.path.append(RTT_ROOT + '/tools')
116+
117+
from building import *
118+
import dtc
119+
120+
dtc.dts_to_dtb(RTT_ROOT, ["dummpy.dts"]
121+
```
122+
123+
Append more include path, like SoC DM directory:
124+
125+
```python
126+
[...]
127+
128+
dtc.dts_to_dtb(RTT_ROOT, ["dummpy.dts"], include_paths = ['dm/include', 'firmware'])
129+
```
130+
131+
Maybe you have other ideas?
132+
133+
#### Multiple DTB
134+
135+
SoC have different SBC, eg. `dummy.dtsi`:
136+
```c
137+
/* SoC dummy */
138+
/ {
139+
#address-cells = <2>;
140+
#size-cells = <2>;
141+
model = "Dummy SoC Board";
142+
143+
[...]
144+
145+
chosen {
146+
bootargs = "cma=8M coherent_pool=2M";
147+
};
148+
149+
reserved-memory {
150+
#address-cells = <2>;
151+
#size-cells = <2>;
152+
153+
isp_shm@100000 {
154+
reg = <0x0 0x100000 0x0 0x100000>;
155+
};
156+
157+
dsp_shm@200000 {
158+
reg = <0x0 0x200000 0x0 0x100000>;
159+
};
160+
};
161+
162+
dsp {
163+
status = "okay";
164+
};
165+
166+
buddy {
167+
isp = <&{/reserved-memory/isp_shm@100000}>;
168+
dsp = <&{/reserved-memory/dsp_shm@200000}>;
169+
};
170+
171+
uart0: uart {
172+
status = "disabled";
173+
};
174+
175+
i2c0: i2c {
176+
status = "okay";
177+
};
178+
179+
[...]
180+
};
181+
```
182+
183+
If you want to enable or disable a device in vendorA:
184+
185+
```c
186+
/* vendorA dummy */
187+
#include "dummy.dtsi"
188+
189+
/ {
190+
/* No phandle name can modify in place */
191+
chosen {
192+
bootargs = "console=uart0 cma=8M coherent_pool=2M";
193+
};
194+
};
195+
196+
/* Reference and modify direct if has phandle name */
197+
&uart0 {
198+
status = "okay";
199+
pinctrl-0 = <&uart0_m1>;
200+
};
201+
202+
&i2c0 {
203+
status = "disabled";
204+
};
205+
```
206+
207+
Some nodes will parse by kernel without "status", disable them by this way:
208+
209+
```c
210+
/* vendorB dummy */
211+
#include "dummy.dtsi"
212+
213+
/delete-node/ &dsp_shm;
214+
215+
/ {
216+
/* Delete in place if no phandle name */
217+
/delete-node/ dsp;
218+
219+
/* Delete property */
220+
buddy {
221+
/delete-property/ dsp;
222+
};
223+
};
224+
```
225+
226+
New device on bus?
227+
228+
```c
229+
/* vendorC dummy */
230+
#include "dummy.dtsi"
231+
232+
&i2c0 {
233+
rtc@0 {
234+
clock-frequency = <32768>;
235+
};
236+
};
237+
```
238+
239+
Build dtb together:
240+
241+
```python
242+
[...]
243+
244+
dtc.dts_to_dtb(RTT_ROOT, ["dummpy-vendorA.dts", "dummpy-vendorB.dts", "dummpy-vendorC.dts"])
245+
```
246+
247+
Will get `dummpy-vendorA.dtb`, `dummpy-vendorB.dtb`, `dummpy-vendorC.dtb`
248+
249+
### Warnings
250+
251+
Irrelevant warnings are annoying, but dtc do it, you can remove them like this:
252+
253+
```python
254+
[...]
255+
256+
dtc.dts_to_dtb(RTT_ROOT, ["dummpy.dts"], ignore_warning = ["simple_bus_reg", "unit_address_vs_reg", "clocks_is_cell", "gpios_property"])
257+
```
258+
259+
**Confirm your dts is right, please :)**
260+
261+
### Raw options
262+
263+
DTC have more raw options than generate dts show by `dtc --help`, like to append them like this:
264+
265+
```python
266+
[...]
267+
268+
dtc.dtb_to_dts(RTT_ROOT, "dummpy.dtb", options = "--quiet")
269+
dtc.dts_to_dtb(RTT_ROOT, ["dummpy.dts"], options = "--quiet ")
270+
```

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+
waring_ops = ""
35+
for waring in ignore_warning:
36+
waring_ops += " -W no-" + waring
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(waring_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)