[TOOLS] Add DTC (Devicetree Compiler) tools

Signed-off-by: GuEe-GUI <2991707448@qq.com>
This commit is contained in:
GuEe-GUI 2025-06-24 10:38:18 +08:00 committed by Rbb666
parent 3e4f0ec015
commit ad2de6e477
4 changed files with 328 additions and 0 deletions

1
.gitignore vendored
View File

@ -11,6 +11,7 @@
*.ilk *.ilk
*.old *.old
*.crf *.crf
*.dtb*
build build
Debug Debug
.vs .vs

View File

@ -13,3 +13,4 @@
- @subpage page_device_wlan - @subpage page_device_wlan
- @subpage page_device_sensor - @subpage page_device_sensor
- @subpage page_device_audio - @subpage page_device_audio
- @subpage page_device_dtc

View File

@ -0,0 +1,274 @@
@page page_device_dtc Devicetree Compiler
# Introduction to the DTC
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.
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.
> If the dtc tool is not installed on your host system, the dtc module will guide you through the installation.
## Generate DTS
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.
You can do this in Python or your SConscript file. For example, assuming you have `dummpy.dtb`:
```python
import os, sys
RTT_ROOT = os.getenv('RTT_ROOT')
sys.path.append(RTT_ROOT + '/tools')
from building import *
import dtc
dtc.dtb_to_dts(RTT_ROOT, "dummpy.dtb")
```
This will generate a dummpy.dts in the current directory. If a file with the same name already exists, it will be replaced.
To avoid overwriting, you can specify a different output name:
```python
[...]
dtc.dtb_to_dts(RTT_ROOT, "dummpy.dtb", "dummpy-tmp.dts")
# or
dtc.dtb_to_dts(RTT_ROOT, "dummpy.dtb", dts_name = "dummpy-tmp.dts")
```
## Generate DTB
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)
### Include and Macros
By default, dtc does not support C-style preprocessing (like cpp), but you can use the C preprocessor with your DTS files.
Don't worry — our dtc module already includes this step.
If your DTS file uses dt-bindings headers or macros, you can write something like:
```c
/*
* Used "#include" if header file need preprocessor,
* `components/drivers/include` and current directory path is default.
*/
#include <dt-bindings/size.h>
#include "dummy.dtsi"
/* Well, if dtsi is simple, you can use "/include/", it is supported by dtc */
/include/ "chosen.dtsi"
#define MMIO_BASE 0x10000
#define MMIO_SIZE SIZE_GB
#define MEM_BASE (MMIO_BASE + MMIO_SIZE)
#ifndef CPU_HARDID
#define CPU_HARDID 0
#endif
#ifndef SOC_INTC
#define SOC_INTC intc_a
#endif
/ {
#address-cells = <2>;
#size-cells = <2>;
/*
* Macros after "&" will be replaced,
* there will affect the interrupt controller in this SoC.
*/
interrupt-parent = <&SOC_INTC>;
[...]
memory {
/* When there is a calculation, please use "()" to include them */
reg = <0x0 MEM_BASE 0x0 (3 * SIZE_GB)>;
device_type = "memory";
};
cpus {
#size-cells = <0>;
#address-cells = <1>;
/* Macros after "@" will be replaced */
cpu0: cpu@CPU_HARDID {
reg = <CPU_HARDID>;
device_type = "cpu";
};
};
/* Macros replace support phandle name, too */
intc_a: intc-a {
interrupt-controller;
};
intc_b: intc-b {
interrupt-controller;
};
[...]
};
```
To generate the DTB:
```python
import os, sys
RTT_ROOT = os.getenv('RTT_ROOT')
sys.path.append(RTT_ROOT + '/tools')
from building import *
import dtc
dtc.dts_to_dtb(RTT_ROOT, ["dummpy.dts"]
```
To append more include paths, for example, SoC DM headers:
```python
[...]
dtc.dts_to_dtb(RTT_ROOT, ["dummpy.dts"], include_paths = ['dm/include', 'firmware'])
```
### Multiple DTB
A single SoC may have different board variants.
Example `dummy.dtsi` (common base):
```c
/* SoC dummy */
/ {
#address-cells = <2>;
#size-cells = <2>;
model = "Dummy SoC Board";
[...]
chosen {
bootargs = "cma=8M coherent_pool=2M";
};
reserved-memory {
#address-cells = <2>;
#size-cells = <2>;
isp_shm@100000 {
reg = <0x0 0x100000 0x0 0x100000>;
};
dsp_shm@200000 {
reg = <0x0 0x200000 0x0 0x100000>;
};
};
dsp {
status = "okay";
};
buddy {
isp = <&{/reserved-memory/isp_shm@100000}>;
dsp = <&{/reserved-memory/dsp_shm@200000}>;
};
uart0: uart {
status = "disabled";
};
i2c0: i2c {
status = "okay";
};
[...]
};
```
For a vendor-specific variant (Vendor A):
```c
/* vendorA dummy */
#include "dummy.dtsi"
/ {
/* No phandle name can modify in place */
chosen {
bootargs = "console=uart0 cma=8M coherent_pool=2M";
};
};
/* Reference and modify direct if has phandle name */
&uart0 {
status = "okay";
pinctrl-0 = <&uart0_m1>;
};
&i2c0 {
status = "disabled";
};
```
To remove nodes or properties (Vendor B):
```c
/* vendorB dummy */
#include "dummy.dtsi"
/delete-node/ &dsp_shm;
/ {
/* Delete in place if no phandle name */
/delete-node/ dsp;
/* Delete property */
buddy {
/delete-property/ dsp;
};
};
```
To add new devices (Vendor C):
```c
/* vendorC dummy */
#include "dummy.dtsi"
&i2c0 {
rtc@0 {
clock-frequency = <32768>;
};
};
```
Build all DTBs together:
```python
[...]
dtc.dts_to_dtb(RTT_ROOT, ["dummpy-vendorA.dts", "dummpy-vendorB.dts", "dummpy-vendorC.dts"])
```
This will produce `dummpy-vendorA.dtb`, `dummpy-vendorB.dtb`, and `dummpy-vendorC.dtb`
## Warnings
DTC may produce warnings that are irrelevant or noisy.
To suppress specific warnings:
```python
[...]
dtc.dts_to_dtb(RTT_ROOT, ["dummpy.dts"], ignore_warning = ["simple_bus_reg", "unit_address_vs_reg", "clocks_is_cell", "gpios_property"])
```
Make sure your DTS is valid!
## Raw options
DTC provides additional command-line options (see dtc --help). You can pass raw options like this:
```python
[...]
dtc.dtb_to_dts(RTT_ROOT, "dummpy.dtb", options = "--quiet")
dtc.dts_to_dtb(RTT_ROOT, ["dummpy.dts"], options = "--quiet")
```

52
tools/dtc.py Normal file
View File

@ -0,0 +1,52 @@
#
# Copyright (c) 2006-2023, RT-Thread Development Team
#
# SPDX-License-Identifier: Apache-2.0
#
# Change Logs:
# Date Author Notes
# 2023-05-10 GuEe-GUI the first version
#
import os, re
from building import *
__dtc_install_tip = """
You should install dtc (devicetree compiler) in your system:
Linux:
Debian/Ubuntu: apt-get install device-tree-compiler
Arch/Manjaro: pacman -Sy dtc
MacOS:
brew install dtc
Windows (MinGW):
msys2: pacman -S dtc
"""
def __check_dtc(value):
if value != 0 and os.system("dtc -v") != 0:
print(__dtc_install_tip)
def dts_to_dtb(RTT_ROOT, dts_list, options = "", include_paths = [], ignore_warning = []):
path = GetCurrentDir() + '/'
warning_ops = ""
for warning in ignore_warning:
warning_ops += " -W no-" + warning
for dts in dts_list:
dtb = dts.replace('.dts', '.dtb')
if not os.path.exists(path + dtb) or os.path.getmtime(path + dtb) < os.path.getmtime(path + dts):
tmp_dts = dts + '.tmp'
Preprocessing(dts, None, output = tmp_dts, CPPPATH=[RTT_ROOT + '/components/drivers/include'] + include_paths)
ret = os.system("dtc -I dts -O dtb -@ -A {} {} {} -o {}".format(warning_ops, options, path + tmp_dts, path + dtb))
__check_dtc(ret)
if os.path.exists(path + tmp_dts):
os.remove(path + tmp_dts)
def dtb_to_dts(RTT_ROOT, dtb_name, dts_name = None, options = ""):
path = GetCurrentDir() + '/'
if dts_name == None:
dts_name = re.sub(r'\.dtb[o]*$', '.dts', dtb_name)
ret = os.system("dtc -I dtb -O dts {} {} -o {}".format(options, path + dtb_name, path + dts_name))
__check_dtc(ret)