-
Notifications
You must be signed in to change notification settings - Fork 5.2k
[TOOLS] Add DTC (Devicetree Compiler) tools #10431
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,6 +11,7 @@ | |
*.ilk | ||
*.old | ||
*.crf | ||
*.dtb* | ||
build | ||
Debug | ||
.vs | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. dts 语法是 json,所以这里感觉用
更好 |
||
/* | ||
* 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 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 我记得更好的做法是,对于 soc 级别的 dtsi 文件中,所有的设备都应该 disabled。在 board level 的 dts 文件中再选择性的 enable 即可。 |
||
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") | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 = []): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 主要来说是加了两个dts/dtb互转的函数?或者是否可以加scons的build? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 并不知道怎么给 scons 添加 build 规则,也没有找到相关资料 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. #!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
高级设备树编译和转换的SCons Builder
支持DTS和DTB文件之间的互相转换,包含更多编译选项
"""
import os
import SCons.Builder
import SCons.Action
import SCons.Util
def dtc_action(target, source, env):
"""DTS到DTB的转换动作"""
dtc = env.get('DTC', 'dtc')
dts_file = str(source[0])
dtb_file = str(target[0])
# 构建命令
cmd_parts = [dtc]
# 添加编译选项
if env.get('DT_FLAGS'):
cmd_parts.extend(env['DT_FLAGS'])
# 添加包含路径
for include_path in env.get('DT_INCLUDES', []):
cmd_parts.extend(['-i', include_path])
# 添加预处理器定义
for define in env.get('DT_DEFINES', []):
cmd_parts.extend(['-D', define])
# 基本参数
cmd_parts.extend(['-I', 'dts', '-O', 'dtb', '-o', dtb_file, dts_file])
cmd = ' '.join(cmd_parts)
return env.Execute(cmd)
def dts_action(target, source, env):
"""DTB到DTS的转换动作"""
dtc = env.get('DTC', 'dtc')
dtb_file = str(source[0])
dts_file = str(target[0])
# 构建命令
cmd_parts = [dtc]
# 添加反编译选项
if env.get('DT_DECOMPILE_FLAGS'):
cmd_parts.extend(env['DT_DECOMPILE_FLAGS'])
# 基本参数
cmd_parts.extend(['-I', 'dtb', '-O', 'dts', '-o', dts_file, dtb_file])
cmd = ' '.join(cmd_parts)
return env.Execute(cmd)
def dt_validate_action(target, source, env):
"""设备树验证动作"""
dtc = env.get('DTC', 'dtc')
dts_file = str(source[0])
cmd = f'{dtc} -I dts -O dtb -f {dts_file}'
return env.Execute(cmd)
def dt_overlay_action(target, source, env):
"""设备树覆盖编译动作"""
dtc = env.get('DTC', 'dtc')
base_dts = str(source[0])
overlay_dts = str(source[1])
output_dtb = str(target[0])
# 先编译基础设备树
base_dtb = str(target[0]) + '.base'
base_cmd = f'{dtc} -I dts -O dtb -o {base_dtb} {base_dts}'
# 编译覆盖
overlay_dtb = str(target[0]) + '.overlay'
overlay_cmd = f'{dtc} -I dts -O dtb -o {overlay_dtb} {overlay_dts}'
# 应用覆盖
apply_cmd = f'{dtc} -I dtb -O dtb -o {output_dtb} -@ {base_dtb} {overlay_dtb}'
return env.Execute(base_cmd) and env.Execute(overlay_cmd) and env.Execute(apply_cmd)
def generate(env):
"""生成高级设备树builder"""
# 创建DTS到DTB的builder
dtc_builder = SCons.Builder.Builder(
action=SCons.Action.Action(dtc_action, 'Compiling DTS to DTB: $TARGET'),
suffix='.dtb',
src_suffix='.dts',
source_scanner=None,
target_scanner=None,
emitter=None
)
# 创建DTB到DTS的builder
dts_builder = SCons.Builder.Builder(
action=SCons.Action.Action(dts_action, 'Decompiling DTB to DTS: $TARGET'),
suffix='.dts',
src_suffix='.dtb',
source_scanner=None,
target_scanner=None,
emitter=None
)
# 创建设备树验证builder
validate_builder = SCons.Builder.Builder(
action=SCons.Action.Action(dt_validate_action, 'Validating DTS: $SOURCE'),
suffix='.valid',
src_suffix='.dts',
source_scanner=None,
target_scanner=None,
emitter=None
)
# 创建设备树覆盖builder
overlay_builder = SCons.Builder.Builder(
action=SCons.Action.Action(dt_overlay_action, 'Applying DTS overlay: $TARGET'),
suffix='.dtb',
src_suffix='.dts',
source_scanner=None,
target_scanner=None,
emitter=None
)
# 将builder添加到环境中
env.Append(BUILDERS={
'DTS2DTB': dtc_builder,
'DTB2DTS': dts_builder,
'DTValidate': validate_builder,
'DTOverlay': overlay_builder
})
# 设置默认的dtc工具路径
if 'DTC' not in env:
dtc_path = env.WhereIs('dtc')
if dtc_path:
env['DTC'] = dtc_path
else:
env['DTC'] = 'dtc'
# 设置默认编译选项
if 'DT_FLAGS' not in env:
env['DT_FLAGS'] = ['-W', 'no-unit_address_vs_reg']
if 'DT_DECOMPILE_FLAGS' not in env:
env['DT_DECOMPILE_FLAGS'] = ['-s']
# 设置默认包含路径
if 'DT_INCLUDES' not in env:
env['DT_INCLUDES'] = []
# 设置默认预处理器定义
if 'DT_DEFINES' not in env:
env['DT_DEFINES'] = []
def exists(env):
"""检查builder是否存在"""
return True 在SConstruct中 # 导入设备树builder
import dt_builder
# 添加基本设备树builder
dt_builder.generate(env)
# 配置DTC工具路径(如果需要自定义路径)
# env['DTC'] = r'C:\path\to\your\dtc.exe'
# 设置高级编译选项
env['DT_FLAGS'] = ['-W', 'no-unit_address_vs_reg', '-W', 'no-graph_port']
env['DT_DECOMPILE_FLAGS'] = ['-s', '--sort']
env['DT_INCLUDES'] = ['./include'] # 如果有include目录
env['DT_DEFINES'] = ['CONFIG_DEBUG=1']
# 基本设备树编译任务
# DTS到DTB转换
env.DTS2DTB('example.dtb', 'example.dts')
# DTB到DTS转换
env.DTB2DTS('example_decompiled.dts', 'example.dtb')
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 整体打包文件 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 后续再改进看看 |
||
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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
takes a device-tree as input in a ......
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copy from here: https://manpages.ubuntu.com/manpages/trusty/man1/dtc.1.html#description