Troubleshooting devicetree

Here are some tips for fixing misbehaving devicetree related code.

See Devicetree HOWTOs for other “HOWTO” style information.

Try again with a pristine build directory


Try this first, before doing anything else.

See Pristine Builds for examples, or just delete the build directory completely and retry.

This is general advice which is especially applicable to debugging devicetree issues, because the outputs are created during the CMake configuration phase, and are not always regenerated when one of their inputs changes.

Make sure <devicetree.h> is included

Unlike Kconfig symbols, the devicetree.h header must be included explicitly.

Many Zephyr header files rely on information from devicetree, so including some other API may transitively include devicetree.h, but that’s not guaranteed.

Make sure you’re using the right names

Remember that:

  • In C/C++, devicetree names must be lowercased and special characters must be converted to underscores. Zephyr’s generated devicetree header has DTS names converted in this way into the C tokens used by the preprocessor-based <devicetree.h> API.

  • In overlays, use devicetree node and property names the same way they would appear in any DTS file. Zephyr overlays are just DTS fragments.

For example, if you’re trying to get the clock-frequency property of a node with path /soc/i2c@12340000 in a C/C++ file:

 * foo.c: lowercase-and-underscores names

/* Don't do this: */
#define MY_CLOCK_FREQ DT_PROP(DT_PATH(soc, i2c@1234000), clock-frequency)
/*                                           ^               ^
 *                                        @ should be _     - should be _  */

/* Do this instead: */
#define MY_CLOCK_FREQ DT_PROP(DT_PATH(soc, i2c_1234000), clock_frequency)
/*                                           ^               ^           */

And if you’re trying to set that property in a devicetree overlay:

 * foo.overlay: DTS names with special characters, etc.

/* Don't do this; you'll get devicetree errors. */
&{/soc/i2c_12340000/} {
     clock_frequency = <115200>;

/* Do this instead. Overlays are just DTS fragments. */
&{/soc/i2c@12340000/} {
     clock-frequency = <115200>;

Look at the preprocessor output

To save preprocessor output when using GCC-based toolchains, add -save-temps=obj to the EXTRA_CFLAGS CMake variable. For example, to build Hello World with west with this option set, use:

west build -b BOARD samples/hello_world -- -DEXTRA_CFLAGS=-save-temps=obj

This will create a preprocessor output file named foo.c.i in the build directory for each source file foo.c.

You can then search for the file in the build directory to see what your devicetree macros expanded to. For example, on macOS and Linux, using find to find main.c.i:

$ find build -name main.c.i

It’s usually easiest to run a style formatter on the results before opening them. For example, to use clang-format to reformat the file in place:

clang-format -i build/CMakeFiles/app.dir/src/main.c.i

You can then open the file in your favorite editor to view the final C results after preprocessing.

Validate properties

If you’re getting a compile error reading a node property, check your node identifier and property. For example, if you get a build error on a line that looks like this:

int baud_rate = DT_PROP(DT_NODELABEL(my_serial), current_speed);

Try checking the node by adding this to the file and recompiling:

#error "whoops"

If you see the “whoops” error message when you rebuild, the node identifier isn’t referring to a valid node. Get your devicetree and generated header and debug from there.

Some hints for what to check next if you don’t see the “whoops” error message:

Check for missing bindings

See Devicetree bindings for information about bindings, and Bindings index for information on bindings built into Zephyr.

If the build fails to Find a devicetree binding for a node, then either the node’s compatible property is not defined, or its value has no matching binding. If the property is set, check for typos in its name. In a devicetree source file, compatible should look like "vnd,some-device"Make sure you’re using the right names.

If your binding file is not under zephyr/dts, you may need to set DTS_ROOT; see Where bindings are located.

Errors with DT_INST_() APIs

If you’re using an API like DT_INST_PROP(), you must define DT_DRV_COMPAT to the lowercase-and-underscores version of the compatible you are interested in. See Option 1: create devices using instance numbers.