zephyrproject-rtos / zephyr

Primary Git Repository for the Zephyr Project. Zephyr is a new generation, scalable, optimized, secure RTOS for multiple hardware architectures.
https://docs.zephyrproject.org
Apache License 2.0
10.11k stars 6.21k forks source link

undefined reference to `__device_dts_ord_xx' #41677

Closed beiszhihao closed 2 years ago

beiszhihao commented 2 years ago

Describe the bug Hi zephyr developers, Hello!

I recently developed a wm89xx coder driver for a development board. My example is drivertree. The registration code is as follows:

#define WM89XX_CONFIG_I2C(inst) \
        {                       \
                .bus.i2c = I2C_DT_SPEC_INST_GET(inst), \
                .bus_io = &wm89xx_bus_io_i2c,           \
        }
#define WM89XX_DEFINE(inst)\
        static struct WM89XX_Data wm89xx_data_##inst; \
        static const struct WM89XX_Config wm89xx_config_##inst = \
                                                WM89XX_CONFIG_I2C(inst); \
        DEVICE_DT_INST_DEFINE(inst,     \
                              wm89xx_init,\
                              NULL,\
                              &wm89xx_data_##inst,\
                              &wm89xx_config_##inst,\
                              POST_KERNEL,\
                              MY_INIT_PRIO,\
                              &wm89xx_api);

DT_INST_FOREACH_STATUS_OKAY(WM89XX_DEFINE)

But I had a problem compiling:

:/home/zhihao/zephyrproject/zephyr/drivers/wm89xx/src/wm89xx.c:39: undefined reference to `__device_dts_ord_92'
collect2: error: ld returned 1 exit status

Remind me undefined reference to `__device_dts_ord_92',I studied it carefully. The problem lies in this registration macro

DEVICE_DT_INST_DEFINE(inst,     \
                              wm89xx_init,\
                              NULL,\
                              &wm89xx_data_##inst,\
                              &wm89xx_config_##inst,\
                              POST_KERNEL,\
                              MY_INIT_PRIO,\
                              &wm89xx_api);

So I went to check the implementation of this macro in the zephyr source code and tried to get some useful information from it

/**
 * @def DEVICE_DT_INST_DEFINE
 *
 * @brief Like DEVICE_DT_DEFINE for an instance of a DT_DRV_COMPAT compatible
 *
 * @param inst instance number. This is replaced by
 * <tt>DT_DRV_COMPAT(inst)</tt> in the call to DEVICE_DT_DEFINE.
 *
 * @param ... other parameters as expected by DEVICE_DT_DEFINE.
 */
#define DEVICE_DT_INST_DEFINE(inst, ...) \
        DEVICE_DT_DEFINE(DT_DRV_INST(inst), __VA_ARGS__)

Through the above definition and description, I probably understand_ device dts ord 92, of which 92 is the ID of the device in the tree, so I think this function is an automatically generated function, so theoretically it should be automatically generated by the zephyr construction tool, but I don't know why this function is not generated for my driver project, so I checked zephyr map

zephyr.map:2282:                0x0000000008003b60                __device_dts_ord_18
zephyr.map:2286:                0x0000000008003b78                __device_dts_ord_274
zephyr.map:2287:                0x0000000008003b90                __device_dts_ord_273
zephyr.map:2288:                0x0000000008003ba8                __device_dts_ord_19
zephyr.map:2289:                0x0000000008003bc0                __device_dts_ord_272
zephyr.map:2290:                0x0000000008003bd8                __device_dts_ord_271
zephyr.map:2291:                0x0000000008003bf0                __device_dts_ord_270
zephyr.map:2292:                0x0000000008003c08                __device_dts_ord_269
zephyr.map:2293:                0x0000000008003c20                __device_dts_ord_268
zephyr.map:2294:                0x0000000008003c38                __device_dts_ord_49
zephyr.map:2295:                0x0000000008003c50                __device_dts_ord_267
zephyr.map:2296:                0x0000000008003c68                __device_dts_ord_69
zephyr.map:2299:                0x0000000008003c80                __device_dts_ord_45
zephyr.map:2302:                0x0000000008003c98                __device_dts_ord_68
zephyr.map:2303:                0x0000000008003cb0                __device_dts_ord_65
zephyr.map:2313:                0x0000000008003ce0                __device_dts_ord_94
zephyr.map:2318:                0x0000000008003d10                __device_dts_ord_42

My function is not imported into the map, so I'm sure the builder didn't generate this for me _ device dts ord function I checked the definition in the DTS file again. Because this coder is used as a slave device of I2C, I declared it under I2C

&i2c3 {
        pinctrl-0 = <&i2c3_scl_ph7 &i2c3_sda_ph8>;
        status = "okay";
        clock-frequency = <I2C_BITRATE_FAST>;
         wm89xx@1a{
                compatible = "micro,wm89xx";
                reg = <0x1a>;
                label = "wm89xx";
                status = "okay";
        };
};

I think there should be no problem with the above DTS definition So I went back to zephyr DEVICE_DT_INST_DEFINE the stage of define source code, I found that it called DEVICE_ DT_ DEFINE, so I went to check its source code again Finally, I located this macro function, which is the final registration driven macro

/* Like DEVICE_DEFINE but takes a node_id AND a dev_name, and trailing
 * dependency handles that come from outside devicetree.
 */
#define Z_DEVICE_DEFINE(node_id, dev_name, drv_name, init_fn, pm_action_cb, \
                        data_ptr, cfg_ptr, level, prio, api_ptr, ...)   \
        Z_DEVICE_DEFINE_PRE(node_id, dev_name, pm_action_cb, __VA_ARGS__) \
        COND_CODE_1(DT_NODE_EXISTS(node_id), (), (static))              \
                const Z_DECL_ALIGN(struct device)                       \
                DEVICE_NAME_GET(dev_name) __used                        \
        __attribute__((__section__(".z_device_" #level STRINGIFY(prio)"_"))) = { \
                .name = drv_name,                                       \
                .config = (cfg_ptr),                                    \
                .api = (api_ptr),                                       \
                .state = &Z_DEVICE_STATE_NAME(dev_name),                \
                .data = (data_ptr),                                     \
                Z_DEVICE_DEFINE_INIT(node_id, dev_name)                 \
        };                                                              \
        BUILD_ASSERT(sizeof(Z_STRINGIFY(drv_name)) <= Z_DEVICE_MAX_NAME_LEN, \
                     Z_STRINGIFY(DEVICE_NAME_GET(drv_name)) " too long"); \
        Z_INIT_ENTRY_DEFINE(DEVICE_NAME_GET(dev_name), init_fn,         \
                (&DEVICE_NAME_GET(dev_name)), level, prio)

From the analysis of the source code, I know that Z_DEVICE_ DEFINE_ P registers driver functions such as power management and uses COND_ CODE_ 1 to determine whether the driver has passed in the power function pointer,Z_DECL_ALIGN For segment alignment It was this macro that finally caught my attention

 Z_DEVICE_DEFINE_INIT(node_id, dev_name) 

Through its macro description, I see such a description:

/* Initial build provides a record that associates the device object
 * with its devicetree ordinal, and provides the dependency ordinals.
 * These are provided as weak definitions (to prevent the reference
 * from being captured when the original object file is compiled), and
 * in a distinct pass1 section (which will be replaced by
 * postprocessing).
 *
 * It is also (experimentally) necessary to provide explicit alignment
 * on each object. Otherwise x86-64 builds will introduce padding
 * between objects in the same input section in individual object
 * files, which will be retained in subsequent links both wasting
 * space and resulting in aggregate size changes relative to pass2
 * when all objects will be in the same input section.
 *
 * The build assert will fail if device_handle_t changes size, which
 * means the alignment directives in the linker scripts and in
 * `gen_handles.py` must be updated.
 */

I noticed this with its devicetree ordinal, and provides the dependency ordinals. And Z_DEVICE_DEFINELike DEVICE_DEFINE but takes a node_id AND a dev_name, and trailing dependency handles that come from outside devicetree. Therefore, I think it needs some associations. My device is declared under the I2C DTS, including some I2C device codes used in my driver. Therefore, I think it is necessary to set some options to enable I2C support, but the status in the DTS has been written as "Okay", so I refer to some compiled examples in samples I refer to the example of sensor/bme280. I find that there is a kconf in this example

config LOG
        default y

config LOG_PRINTK
        default y

config SENSOR_LOG_LEVEL
        default 4

# Enable SPI and I2C support by default so that the sample works with
# the device connected either way. These defaults can be overridden if
# needed.
config SPI
        default y

config I2C
        default y

source "Kconfig.zephyr"

I began to try it. I found that when I set config I2C to N, the same problem as me will appear, which surprised me very much, because this problem has made progress. When I set I2C to y in my driver kconfig, my project was surprisingly compiled and passed, and there will be no previous problems. So I guess the reason is Z_DEVICE_DEFINE The macro define gets the properties of the dependent device node, where it uses COND_ CODE_ 1 this macro performs conditional compilation. If the dependent node function has no driver, zephyr will not compile the I2C device. If the dependent device driver has not been compiled, zephyr will not generate it__ device_ dts_ ord function

In order to further verify my conjecture, my Prj.confin the app project Add config to CONF_I2C=y, it is found that the effect is the same as that in kconfig, so my conclusion is basically determined. The above is my debugging process and solution to this problem. When this problem occurred, I tried to search for information on the Internet and found that someone also encountered this problem, but there was no solution,So I think it is necessary to submit this question to the issue

Best Regards, Stephen Zhou

mbolivar-nordic commented 2 years ago

Hi there @beiszhihao ! Thanks for the detailed report. I need to confirm 2 things with you.

  1. Do you have this line of code:
#define DT_DRV_COMPAT micro_wm89xx

In the same file you are using DT_INST_FOREACH_STATUS_OKAY? You must define DT_DRV_COMPAT for this to work.

Please see this documentation for details and examples for how to use DT_INST_FOREACH_STATUS_OKAY: https://docs.zephyrproject.org/latest/guides/dts/howtos.html#option-1-create-devices-using-instance-numbers

  1. Do you have a devicetree binding written for the "micro,wm89xx" compatible? See here for more information about bindings: https://docs.zephyrproject.org/latest/guides/dts/bindings.html#dt-bindings
beiszhihao commented 2 years ago

area: Devicetree priority: low

Hi @mbolivar-nordic Thank you for your reply

I make sure I'm using DT_ INST_ FOREACH_ STATUS_ OKAY source code file declares DT_DRV_COMPAT and ensure that it is associated with drivertree I used DT_ NUM_ INST_ STATUS_ OKAY

#if DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) == 0
#error"WM89xx series coder is not defined in DTS"
#endif
ceolin commented 2 years ago

I saw this happen in a different scenario happens with power domain. In the follow example you have a domain and two devices using it. The DT will generate aa dependency between them, but the domain may not be defined if we don't build with this feature enabled. In this situation the same problem will happen, we will have an undefined reference ...

/ {
    test_domain: test_domain {
        compatible = "power-domain";
        status = "okay";
    };

    test_dev_a: test_dev_a {
        compatible = "test-device-pm";
        status = "okay";
        power-domain = <&test_domain>;
    };

    test_dev_b: test_dev_b {
        compatible = "test-device-pm";
        status = "okay";
        power-domain = <&test_domain>;
    };
};

So, if I understood correctly, we can't have a device "depending" on other that is not defined, even if this dependency is optional.

mbolivar-nordic commented 2 years ago

@beiszhihao I know you have given me a detailed report, but I think there may be some missing piece of information. If you are still having problems, could you please provide a git branch that reproduces the issue, along with a west build command that shows how it happens? The undefined reference to __device... is a generic problem that happens if you try to get a device pointer from a devicetree node if no such device exists. The underlying infrastructure is well tested so I suspect there is a misconfiguration in the driver itself.

jeremyherbert commented 2 years ago

hi @mbolivar-nordic I am having this exact same problem. I have created a repo here which just implements a simple fake accelerometer sensor driver, if you just clone and then west build (board is set inside the CMakeLists.txt file) you will see the error:

src/main.c:30: undefined reference to__device_dts_ord_3'`

I am unsure if the cause is related, but if you could point me in the right direction for how to debug these DTS binding issues it would be greatly appreciated.

jeremyherbert commented 2 years ago

I have solved my error. There was a spelling mistake in the DRV_COMPAT declaration for my custom driver (although to be fair, this isn't exactly a helpful error message for someone who doesn't deeply understand the zephyr build system)

josuah commented 5 months ago

No issue with this, but for whoever finding this issue from a search engine, here is the Zephyr documentation about it: https://docs.zephyrproject.org/latest/build/dts/troubleshooting.html Thanks!

pkoetter commented 2 months ago

Thanks for your input! I had a similar problem after using the example-application as a blueprint for my own project. However the issue I found was that in the file <your_repository_with_west.yml>/zephyr/module.yml (see example) the line dts_root: . was previously commented by me. Uncommenting it probably made the /dts/bindings folder visible to the build system.