summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuEe-GUI <[email protected]>2025-06-24 10:38:18 +0800
committerRbb666 <[email protected]>2025-06-30 09:42:39 +0800
commitad2de6e477cad1da7f5c930f4b0f0945fd67a0cd (patch)
tree179d90369758396503bb8b96b23ab3f8c5aede7e
parent3e4f0ec015d6ad8e8c1f43b01b33efe2f6e12b68 (diff)
[TOOLS] Add DTC (Devicetree Compiler) tools
Signed-off-by: GuEe-GUI <[email protected]>
-rw-r--r--.gitignore1
-rw-r--r--documentation/6.components/device-driver/INDEX.md1
-rw-r--r--documentation/6.components/device-driver/ofw/dtc.md274
-rw-r--r--tools/dtc.py52
4 files changed, 328 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index 72a8882ed4..df7cd55d87 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,6 +11,7 @@
*.ilk
*.old
*.crf
+*.dtb*
build
Debug
.vs
diff --git a/documentation/6.components/device-driver/INDEX.md b/documentation/6.components/device-driver/INDEX.md
index 010d0fc910..b8600c9e79 100644
--- a/documentation/6.components/device-driver/INDEX.md
+++ b/documentation/6.components/device-driver/INDEX.md
@@ -13,3 +13,4 @@
- @subpage page_device_wlan
- @subpage page_device_sensor
- @subpage page_device_audio
+- @subpage page_device_dtc
diff --git a/documentation/6.components/device-driver/ofw/dtc.md b/documentation/6.components/device-driver/ofw/dtc.md
new file mode 100644
index 0000000000..2cc3c1b34c
--- /dev/null
+++ b/documentation/6.components/device-driver/ofw/dtc.md
@@ -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")
+```
diff --git a/tools/dtc.py b/tools/dtc.py
new file mode 100644
index 0000000000..67d7d04f61
--- /dev/null
+++ b/tools/dtc.py
@@ -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)