What are Compiling Device Trees With dtc-Embedded Linux Training In Hyderabad

PREV_LEC NEXT_LEC

Compiling Device Trees With dtc
From .dts source to the .dtb binary blob the bootloader hands the kernel — free embedded linux bootloader course
Chapter 3 · Lecture 9
Free Linux Kernel Development Course
Beginner to Intermediate

Everything we’ve covered so far in this chapter — nodes, the reg property, phandles, include files — exists in human-readable .dts/.dtsi source. But the kernel never reads that text directly. Somewhere between your source files and the running kernel, a tool called the device tree compiler, dtc, turns all of it into a compact binary format that the bootloader can load quickly and the kernel can parse without a text parser at boot time. This lecture closes out the device tree portion of this free embedded systems course by walking through that compilation step in real detail.

Keywords covered in this lecture
device tree compiler dtc
dts to dtb
flattened device tree
device tree blob
dtc command line options
free linux device drivers course

What You Will Learn

  • What dtc is and where it lives, both in the kernel tree and as a standalone package
  • The full pipeline from .dts/.dtsi source to a bootable .dtb
  • How to compile a device tree with includes, and how the kernel build system automates this
  • Useful dtc command-line flags for warnings, symbols, and decompilation
  • How to sanity-check a compiled .dtb before flashing it to a board

Prerequisites

This lecture assumes familiarity with device tree source syntax, the reg property, phandles, and include files from the previous three lectures in this free linux kernel development course.

What dtc Actually Does

The device tree compiler reads one or more .dts/.dtsi source files (already flattened by the C preprocessor if they use #include), validates their syntax, resolves every &label reference into a numeric phandle, and emits a compact, self-describing binary format called the Flattened Device Tree (FDT), stored in a file conventionally ending in .dtb — a device tree blob.

You’ll find a copy of dtc inside the Linux kernel source tree itself, at scripts/dtc/dtc, built automatically as part of a normal kernel build. It’s also packaged standalone on most Linux distributions, which is handy for compiling and inspecting device trees without a full kernel build tree on hand.

Installing dtc as a standalone package
$ sudo apt install device-tree-compiler
$ dtc –version
Version: DTC 1.7.0

Compiling a Simple Device Tree

For a self-contained source file that doesn’t use #include, compiling is a single direct command:

Compiling a standalone .dts with no includes
$ dtc ep-simple.dts -o ep-simple.dtb
DTC: dts->dts on file “ep-simple.dts”
$ file ep-simple.dtb
ep-simple.dtb: Device Tree Blob version 17, size=1240, boot CPU=0, string block size=142, DT structure block size=1032

Compiling a Device Tree That Uses #include

The moment your source uses C-style #include (covered in the previous lecture), you need to run it through the C preprocessor first — dtc itself doesn’t understand #include or #define, only the flattened Open Firmware /include/ form. This is exactly what the kernel’s own build system automates for you when you run a normal kernel build, but it’s useful to see the manual two-step to understand what’s happening underneath:

Manual two-step: preprocess, then compile
$ cpp -nostdinc -I include -undef -x assembler-with-cpp \
ep-demo-board.dts ep-demo-board.pre.dts$ dtc -I dts -O dtb ep-demo-board.pre.dts -o ep-demo-board.dtb
DTC: dts->dts on file “ep-demo-board.pre.dts”

In an actual kernel build tree, you never run these two commands by hand — the kernel’s Makefile-based build system (kbuild) does this automatically for every .dts file listed in your platform’s Makefile:

Building all device trees for a target as part of a kernel build
$ make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- dtbs
DTC arch/arm64/boot/dts/ep/ep-demo-board.dtb$ ls arch/arm64/boot/dts/ep/*.dtb
arch/arm64/boot/dts/ep/ep-demo-board.dtb

Useful dtc Command-Line Options

Flag Purpose
-I <format> Input format: dts (source text) or dtb (binary, for decompiling)
-O <format> Output format: dtb (compile) or dts (decompile)
-o <file> Output file path
-W <warning> Enable/disable a specific class of warning (e.g. unit-address mismatches)
-@ Generate a __symbols__ node, needed for device tree overlays to resolve phandles at runtime
-f Force output even if the compiler detected non-fatal errors
Compiling with warnings enabled, useful while developing a new board file
$ dtc -W unit_address_vs_reg -W node_name_chars \
ep-demo-board.pre.dts -o ep-demo-board.dtb
ep-demo-board.pre.dts:14.5-18: Warning (unit_address_vs_reg): \
node has a reg or ranges property, but no unit name

That particular warning is one of the most common ones beginners hit: if a node’s name includes a @unit-address suffix, the address portion must match the first value in its reg property exactly. A mismatch here almost always means a typo, and it’s worth fixing rather than ignoring.

Decompiling: Reading a .dtb Back as Text

We’ve used this already in the previous lecture, but it deserves its own callout here: dtc works in both directions. Feeding it a .dtb as input and asking for .dts output lets you inspect exactly what a bootloader or kernel is actually working with — including a .dtb you didn’t build yourself, such as one already flashed on a board.

Decompiling a .dtb pulled from a running board’s /sys
$ dtc -I dtb -O dts /sys/firmware/devicetree/base -o live-tree.dts
$ head -n 15 live-tree.dts
/dts-v1/;/ {
compatible = “ep,demo-board”, “ep,socx”;
model = “EP Demo Board Rev A”;
#address-cells = <0x02>;
#size-cells = <0x02>;
…

This is one of the fastest ways to confirm exactly what device tree a board actually booted with, versus what you think you built and flashed — the two are not always the same, especially when a bootloader applies its own overlay on top before handing control to the kernel.

A Minimal Build-and-Boot Sanity Check

Here’s a small, complete original example you can run end to end to see every stage of the pipeline for yourself, using a fictional demo node rather than any real board’s tree:

ep-check.dts — minimal standalone tree with one demo device
/dts-v1/;/ {
compatible = “ep,check-demo”;
#address-cells = <1>;
#size-cells = <1>;demo@60000000 {
compatible = “ep,demo-block”;
reg = <0x60000000 0x1000>;
status = “okay”;
};
};
Full round trip: compile, decompile, diff against source
$ dtc ep-check.dts -o ep-check.dtb
$ dtc -I dtb -O dts ep-check.dtb -o ep-check.roundtrip.dts
$ diff <(grep -v ‘^/’ ep-check.dts) <(grep -v ‘^/’ ep-check.roundtrip.dts)
$ echo $?
0

A clean diff (aside from the auto-generated header comments dtc adds on decompile) confirms the compiler preserved your intent exactly — a useful habit any time you’re debugging whether a device tree bug is in your source or somewhere later in the boot pipeline.

Where the .dtb Goes Next

Once compiled, the .dtb doesn’t get linked into the kernel image itself in most workflows — it’s a separate file that the bootloader loads into memory alongside the kernel and passes a pointer to, which is exactly the handoff mechanism we covered earlier in this bootloader course. Some platforms embed the .dtb directly appended to the kernel image instead; check your specific bootloader’s boot script to see which convention your platform uses.

Common Mistakes and Troubleshooting

  • Running dtc directly on a file with #include. Without preprocessing first, dtc will fail on the raw #include/#define lines it doesn’t understand — always preprocess first, or let kbuild handle it.
  • Ignoring unit-address warnings. A unit_address_vs_reg warning almost always points at a real typo between a node’s @address suffix and its reg property.
  • Forgetting -@ when building a base tree meant to host overlays. Without the generated __symbols__ node, overlay files can’t resolve phandle references into the base tree at runtime.
  • Assuming the flashed .dtb matches your source. Always decompile the live tree from the board itself when debugging, rather than trusting that what you built is what’s actually running.

Best Practices

  • Let kbuild handle preprocessing and compilation for in-tree boards; only invoke cpp/dtc manually when debugging the pipeline itself.
  • Enable relevant -W warnings during development of a new board file, and treat them as real bugs, not noise.
  • Keep a habit of decompiling your own compiled output to confirm it matches your intent before flashing to hardware.
  • Use -@ on any base tree you expect to extend with device tree overlays later.

Summary and Key Takeaways

  • dtc, the device tree compiler, turns human-readable .dts/.dtsi source into a binary Flattened Device Tree (.dtb).
  • Source using C-style #include/#define must be run through cpp first — kbuild automates this for in-tree builds.
  • dtc works both directions: -I dts -O dtb to compile, -I dtb -O dts to decompile.
  • Enabling -W warnings during development catches common mistakes like unit-address mismatches early.
  • Always verify what’s actually running on a board by decompiling its live tree, not just trusting your build output.

Conclusion

With this lecture, we’ve followed the device tree all the way from hand-written source to the binary blob a bootloader hands off to a booting Linux kernel — completing the device tree portion of this free embedded linux bootloader course. The reg property, phandles, include files, and compilation together form a complete, if compact, hardware description language, and being fluent in reading and building it is one of the most transferable skills in embedded Linux bring-up work. Next in this free embedded systems course, we’ll pick up where the kernel takes over after this handoff and look at early kernel boot.

Frequently Asked Questions

What does the device tree compiler dtc actually produce?

A compact binary format called the Flattened Device Tree, stored in a .dtb file, compiled from human-readable .dts/.dtsi source text.

Where is dtc located in the Linux kernel source?

At scripts/dtc/dtc, built automatically as part of a normal kernel build; it’s also available as a standalone package on most distributions.

Why can’t dtc directly compile a .dts file that uses #include?

Because dtc only understands the Open Firmware /include/ directive, not C-style #include/#define; those need to be resolved by the C preprocessor first.

How do I decompile a .dtb back into readable text?

Run dtc -I dtb -O dts on the binary file, which produces a text representation of exactly what’s encoded in the blob.

What does the -@ flag do when compiling a device tree?

It generates a __symbols__ node in the output, which device tree overlays need to resolve phandle references into the base tree at runtime.

What does a unit_address_vs_reg warning from dtc mean?

It means a node’s @unit-address suffix doesn’t match the first value in its reg property, almost always indicating a typo.

Does the kernel build system run dtc automatically?

Yes — running a target like make dtbs invokes preprocessing and dtc automatically for every device tree file listed in the platform’s build files.

 

Continue the Free Embedded Linux Bootloader Course

That completes the Device Tree topic — next, we move into what happens as the Linux kernel takes over after handoff.

PREV_LEC NEXT_LEC

Leave a Reply

Your email address will not be published. Required fields are marked *