Skip to main content
Planet Zephyr

Zephyr Devicetree Troubleshooting: Generated Files

By June 15, 2023No Comments

Learning Devicetree is one of the more difficult parts of getting comfortable with Zephyr. I find the error messages can be extremely long and hard to decipher. One tool that has helped me along the way is the ability to look at the header files that are being generated when the Devicetree files are combined at build time. Every project has a build/include/generated/devicetree_generated.h file that you can reference against error messages.

Let’s look an example of a common Devicetree error message and how I might troubleshoot it.

Building zephyr/samples/sensor/bme280

The Bosche BME280 sensor is one of our favorites here at Golioth. Let’s build the Zephyr sample application for that sensor, using a Nordic nRF9160-DK. The only change we need to make is to add an overlay file for this board:

/* Warning: we've made an error in this file for the demo */
&i2c3 {
    pinctrl-0 = ;
    pinctrl-1 = ;
    pinctrl-names = "default", "sleep";

    bme280@77 {
        compatible = "bosch,bme280";
        reg = ;
    };
};

&pinctrl {
    i2c2_default: i2c2_default {
        group1 {
            psels = ,
                ;
        };
    };

    i2c2_sleep: i2c2_sleep {
        group1 {
            psels = ,
                ;
            low-power-enable;
        };
    };
};

Now if you try to build this application:

$ west build -b nrf9160dk_nrf9160_ns . -p

You will eventually be greeted by dozens of lines of error output. The part of the error I usually look at most closely is the line that actually says error in it:

/home/mike/golioth-ncs-workspace/zephyr/include/zephyr/device.h:83:41:
 error: '__device_dts_ord_109' undeclared here (not in a function);
 did you mean '__device_dts_ord_19'?                                                                                                     
   83 | #define DEVICE_NAME_GET(dev_id) _CONCAT(__device_, dev_id)                                                                       
      |                                         ^~~~~~~~~

Now __device_dts_ord_109 is certainly not part of my code. But I recognize the format as belonging to the Devicetree build process. Let’s see if we can make more sens of that identifier.

Troubleshooting Devicetree with generated header files

Look in the build/include/generated/devicetree_generated.h file that was generated during the build process. Near the top, in comments for this file, you will find the Node dependency ordering list.

* Node dependency ordering (ordinal and path):
*   0   /
*   1   /aliases
*   2   /analog-connector
*   3   /chosen
*   4   /connector
*   5   /entropy_bt_hci
*   6   /gpio-interface
*   7   /soc
*   8   /soc/peripheral@40000000
*   9   /soc/peripheral@40000000/gpio@842500
*   10  /gpio-reset
 
... 96 lines removed for blog post brevity ...
 
*   107 /soc/peripheral@40000000/flash-controller@39000/flash@0/partitions/partition@f0000
*   108 /soc/peripheral@40000000/flash-controller@39000/flash@0/partitions/partition@fa000
*   109 /soc/peripheral@40000000/i2c@b000
*   110 /soc/peripheral@40000000/i2c@b000/bme280@77

Okay, now we’re getting somewhere! I can see in the list above that node 109 (the number that appeared in the error message) is an i2c bus, and node 110 is our BME280 node on that i2c bus. So the error we’re getting relates in some way to this node being undeclared.

The easiest way to look at the declaration of the nodes is to view the build/zephyr/zephyr.dts file that is the combination of all Devicetree files during the build process:

i2c3: i2c@b000 {
    compatible = "nordic,nrf-twim";
    #address-cells = ;
    #size-cells = ;
    reg = ;
    clock-frequency = ;
    interrupts = ;
    status = "disabled";
    pinctrl-0 = ;
    pinctrl-1 = ;
    pinctrl-names = "default", "sleep";
    bme280@77 {
        compatible = "bosch,bme280";
        reg = ;
    };
};

I was able to find i2c@b000 in this file. It corresponds to the i2c3 node I want to use for my sensor. And indeed, you can see the sensor node is present. So why can’t the build system locate this node? The answer is in line 264: status = "disabled“.

Disabled nodes are not included in the build. So even though we see information here, the preprocessor will not include symbols for this node. If we want to use this peripheral, we need to enable it. That is the mistake I made in my overlay file.

Correcting the Overlay File

Correcting the overlay file is a simple matter of enabling our target node. If you’re like me, you might assume the opposite of disabled is enabled, but you would be wrong. Zephyr wants enabled nodes to use the okay keyword:

&i2c3 {
    status = "okay";
    pinctrl-0 = ;
    pinctrl-1 = ;
    pinctrl-names = "default", "sleep";
    bme280@77 {
        compatible = "bosch,bme280";
        reg = ;
    };
};

&spi3 {
    /* The nRF9160 cannot have both
     * i2c3 and spi3 enabled concurrently */

    status = "disabled";
};

&pinctrl {
    i2c2_default: i2c2_default {
        group1 {
            psels = ,
                ;
        };
    };

    i2c2_sleep: i2c2_sleep {
        group1 {
            psels = ,
                ;
            low-power-enable;
        };
    };
};

When solving this issue I also received an error after enabling i2c3 because spi3 was already enabled by default. This device can only have one of those enabled at a time, which explains the additional node above that disables the unused SPI bus.

Conclusion

Understanding Devicetree errors is a bit like playing jazz. There’s a pattern to it, but you do need to develop a bit of a feel for it. That begins with developing a sense for what the error output is telling you. I hope this tidbit will make things a bit easier.

If you have other Zephyr troubleshooting tricks we should know about, we’d love to hear it! Please share your experiences on the Golioth Forum!

Zephyr Project