[TOOLS] Add DTC (Devicetree Compiler) tools

Signed-off-by: GuEe-GUI <2991707448@qq.com>
This commit is contained in:
GuEe-GUI
2025-06-30 09:42:39 +08:00
committed by Rbb666
parent 3e4f0ec015
commit ad2de6e477
4 changed files with 328 additions and 0 deletions
@@ -13,3 +13,4 @@
- @subpage page_device_wlan
- @subpage page_device_sensor
- @subpage page_device_audio
- @subpage page_device_dtc
@@ -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")
```