Skip to content
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

undefined reference to `__device_dts_ord_xx' #41677

Closed
beiszhihao opened this issue Jan 10, 2022 · 9 comments
Closed

undefined reference to `__device_dts_ord_xx' #41677

beiszhihao opened this issue Jan 10, 2022 · 9 comments
Assignees

Comments

@beiszhihao
Copy link

beiszhihao commented Jan 10, 2022

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.conf in 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
Copy link
Contributor

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

@mbolivar-nordic mbolivar-nordic added question and removed bug The issue is a bug, or the PR is fixing a bug labels Jan 13, 2022
@beiszhihao
Copy link
Author

beiszhihao commented Jan 13, 2022

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

@henrikbrixandersen henrikbrixandersen removed the priority: low Low impact/importance bug label Jan 22, 2022
@ceolin
Copy link
Member

ceolin commented Jan 29, 2022

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
Copy link
Contributor

@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
Copy link
Contributor

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
Copy link
Contributor

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
Copy link
Collaborator

josuah commented Jan 27, 2024

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
Copy link

pkoetter commented May 8, 2024

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 <your_repository_with_west.yml>/dts/bindings folder visible to the build system.

@gjhave
Copy link

gjhave commented Sep 29, 2024

Nice share, I got same issue, but cause by different mistake, share with you:
in ".yaml" and ".dts" files:
compatible: "sc7a20-sensor"
in "*_driver.c" files:
#define DT_DRV_COMPAT sc7a20-sensor
#define INST_DT_SC7A20_SENSOR(inst) DT_INST(inst, sc7a20-sensor)

change sc7a20-sensor to sc7a20_sensor in "*_driver.c" files and the problem will be solved.
#define DT_DRV_COMPAT sc7a20_sensor
#define INST_DT_SC7A20_SENSOR(inst) DT_INST(inst, sc7a20_sensor)

The reason is "-" is illegal in C/C++

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

10 participants