[{"data":1,"prerenderedAt":523},["ShallowReactive",2],{"developer-note-zephyr-kconfig-controlled-devicetree-instances":3},{"id":4,"title":5,"author":6,"body":7,"date":515,"description":516,"extension":517,"meta":518,"navigation":373,"path":519,"seo":520,"stem":521,"__hash__":522},"developerNotes\u002Fdevelopers\u002Fnotes\u002Fzephyr-kconfig-controlled-devicetree-instances.md","Zephyr: Kconfig controlled devicetree instances","Jordan Yates",{"type":8,"value":9,"toc":512},"minimark",[10,21,49,52,59,72,75,80,83,212,243,259,271,280,326,332,343,349,394,403,408,414,436,441,505,508],[11,12,13],"p",{},[14,15],"img",{"alt":16,"className":17,"src":19,"width":20},"Embeint Blog Pictures",[18],"rounded-lg","\u002Fimages\u002Fblog\u002F20240703.zephyr-kconfig-controlled-devicetree-instances-1.png",1200,[11,22,23,24,31,32,37,38,42,43,48],{},"If you develop with Zephyr, then you're likely familiar with ",[25,26,30],"a",{"href":27,"rel":28},"https:\u002F\u002Fdocs.zephyrproject.org\u002Flatest\u002Fbuild\u002Fdts\u002Fapi-usage.html#node-identifiers",[29],"nofollow","devicetree nodes"," and the ",[25,33,36],{"href":34,"rel":35},"https:\u002F\u002Fdocs.zephyrproject.org\u002Flatest\u002Fbuild\u002Fdts\u002Fapi\u002Fapi.html#c.DT_INST_FOREACH_STATUS_OKAY",[29],"DT_INST_FOREACH_STATUS_OKAY"," macro. The purpose of the macro is to iterate over a list of devicetree nodes with ",[39,40,41],"code",{},"status = \"okay\""," and create a ",[25,44,47],{"href":45,"rel":46},"https:\u002F\u002Fdocs.zephyrproject.org\u002Flatest\u002Fkernel\u002Fdrivers\u002Findex.html",[29],"Zephyr device"," for each. This allows you to instantiate an arbitrary number of nodes from devicetree without modifying any C code (happy times).",[11,50,51],{},"Devicetree nodes can refer to other nodes in the devicetree, with the C code obtaining compile-time pointers to those devices to use in future API calls. However, if the device you have a reference to is not compiled into the final application, then you will end up with a linker error instead of a pointer (sad times).",[11,53,54,58],{},[55,56,57],"strong",{},"Zephyr build system has a strict processing order",":",[60,61,62,66,69],"ol",{},[63,64,65],"li",{},"Devicetree is completely parsed",[63,67,68],{},"Kconfig is evaluated",[63,70,71],{},"C code compiles",[11,73,74],{},"There is no way to modify the content of the devicetree based on the Kconfig options that the application selects.",[11,76,77],{},[55,78,79],{},"One potential challenge that can arise when writing generic libraries is that the various backends for a common API often have different dependencies, resulting in different combinations of devices compiled into the final binary depending on the Kconfig setup.",[11,81,82],{},"Take the following devicetree snippet as an example:",[84,85,90],"pre",{"className":86,"code":87,"language":88,"meta":89,"style":89},"language-dts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u002F* CONFIG_VND_TX_UDP, depends on CONFIG_NETWORKING *\u002F\ntx_udp {\n    compatible = \"vnd,tx\";\n    status = \"okay\";\n};\n\u002F* CONFIG_VND_TX_SERIAL, depends on CONFIG_SERIAL *\u002F\ntx_serial {\n    compatible = \"vnd,tx\";\n    status = \"okay\";\n};\n\u002F* CONFIG_VND_LOGGER, Generic logging abstraction *\u002F\nlogger_udp {\n    compatible = \"vnd,logger\";\n    status = \"okay\";\n    backend = \u003C &tx_udp >;\n};\nlogger_serial {\n    compatible = \"vnd,logger\";\n    status = \"okay\";\n    backend = \u003C &tx_serial >;\n};\n","dts","",[39,91,92,100,106,112,118,124,130,136,141,146,151,157,163,169,174,180,185,191,196,201,207],{"__ignoreMap":89},[93,94,97],"span",{"class":95,"line":96},"line",1,[93,98,99],{},"\u002F* CONFIG_VND_TX_UDP, depends on CONFIG_NETWORKING *\u002F\n",[93,101,103],{"class":95,"line":102},2,[93,104,105],{},"tx_udp {\n",[93,107,109],{"class":95,"line":108},3,[93,110,111],{},"    compatible = \"vnd,tx\";\n",[93,113,115],{"class":95,"line":114},4,[93,116,117],{},"    status = \"okay\";\n",[93,119,121],{"class":95,"line":120},5,[93,122,123],{},"};\n",[93,125,127],{"class":95,"line":126},6,[93,128,129],{},"\u002F* CONFIG_VND_TX_SERIAL, depends on CONFIG_SERIAL *\u002F\n",[93,131,133],{"class":95,"line":132},7,[93,134,135],{},"tx_serial {\n",[93,137,139],{"class":95,"line":138},8,[93,140,111],{},[93,142,144],{"class":95,"line":143},9,[93,145,117],{},[93,147,149],{"class":95,"line":148},10,[93,150,123],{},[93,152,154],{"class":95,"line":153},11,[93,155,156],{},"\u002F* CONFIG_VND_LOGGER, Generic logging abstraction *\u002F\n",[93,158,160],{"class":95,"line":159},12,[93,161,162],{},"logger_udp {\n",[93,164,166],{"class":95,"line":165},13,[93,167,168],{},"    compatible = \"vnd,logger\";\n",[93,170,172],{"class":95,"line":171},14,[93,173,117],{},[93,175,177],{"class":95,"line":176},15,[93,178,179],{},"    backend = \u003C &tx_udp >;\n",[93,181,183],{"class":95,"line":182},16,[93,184,123],{},[93,186,188],{"class":95,"line":187},17,[93,189,190],{},"logger_serial {\n",[93,192,194],{"class":95,"line":193},18,[93,195,168],{},[93,197,199],{"class":95,"line":198},19,[93,200,117],{},[93,202,204],{"class":95,"line":203},20,[93,205,206],{},"    backend = \u003C &tx_serial >;\n",[93,208,210],{"class":95,"line":209},21,[93,211,123],{},[11,213,214,215,218,219,222,223,226,227,230,231,234,235,238,239,242],{},"We have a generic logging abstraction ",[39,216,217],{},"vnd,logger"," with each node referring to a specific transmission backend, as well as two transmission instances that transmit payloads over communications stacks. If an application enables ",[39,220,221],{},"CONFIG_NETWORKING"," then ",[39,224,225],{},"CONFIG_VND_TX_UDP"," is enabled and ",[39,228,229],{},"tx_udp"," exists in our build. The same applies to ",[39,232,233],{},"CONFIG_SERIAL"," with ",[39,236,237],{},"CONFIG_VND_TX_SERIAL"," and ",[39,240,241],{},"tx_serial",".",[11,244,245,246,249,250,253,254,238,256,258],{},"Unfortunately, a problem arises when ",[39,247,248],{},"CONFIG_VND_LOGGER"," is enabled. Each logger will attempt to get a compile-time reference to the ",[39,251,252],{},"vnd,tx"," devices, but this will only successfully link if both ",[39,255,221],{},[39,257,233],{}," are enabled. Not every application may want the networking stack compiled in (even if the hardware supports it).",[11,260,261,262,238,264,267,268,270],{},"The typical \"solution\" is to ensure that ",[39,263,229],{},[39,265,266],{},"logger_udp"," don’t exist in the devicetree if ",[39,269,221],{}," is not enabled by the application. The downside of this approach is that it requires the application to have an overlay file for every board that it could compile with, to indicate whether to add required nodes or delete unnecessary ones. This does not scale well with supporting new or out-of-tree hardware platforms (trust me, sad times).",[11,272,273,274,276,277,279],{},"A more scalable approach I employ for solving this problem is enabling the ",[39,275,217],{}," nodes (not the application) to determine whether their backend dependency will be compiled into the build and skipping the instantiation if not. This requires adding a piece of Kconfig information into the devicetree node that can then be queried within ",[39,278,36],{},". For the UDP backend example above, the approach will look like the following:",[84,281,283],{"className":86,"code":282,"language":88,"meta":89,"style":89},"tx_udp {\n    compatible = \"vnd,tx\";\n    status = \"okay\";\n    depends-on = \"CONFIG_VND_TX_UDP\";\n};\nlogger_udp {\n    compatible = \"vnd,logger\";\n    status = \"okay\";\n    backend = \u003C &tx_udp >;\n};\n",[39,284,285,289,293,297,302,306,310,314,318,322],{"__ignoreMap":89},[93,286,287],{"class":95,"line":96},[93,288,105],{},[93,290,291],{"class":95,"line":102},[93,292,111],{},[93,294,295],{"class":95,"line":108},[93,296,117],{},[93,298,299],{"class":95,"line":114},[93,300,301],{},"    depends-on = \"CONFIG_VND_TX_UDP\";\n",[93,303,304],{"class":95,"line":120},[93,305,123],{},[93,307,308],{"class":95,"line":126},[93,309,162],{},[93,311,312],{"class":95,"line":132},[93,313,168],{},[93,315,316],{"class":95,"line":138},[93,317,117],{},[93,319,320],{"class":95,"line":143},[93,321,179],{},[93,323,324],{"class":95,"line":148},[93,325,123],{},[11,327,328,329,331],{},"The ",[39,330,252],{}," API can now expose this information as a 0 or 1 through a macro such as:",[84,333,337],{"className":334,"code":335,"language":336,"meta":89,"style":89},"language-c shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","#define VND_TX_IS_COMPILED(tx_node) IS_ENABLED(DT_STRING_TOKEN(tx_node, depends_on))\n","c",[39,338,339],{"__ignoreMap":89},[93,340,341],{"class":95,"line":96},[93,342,335],{},[11,344,345,346,348],{},"And the ",[39,347,217],{}," node can consume this information at compile-time with the following pattern:",[84,350,352],{"className":334,"code":351,"language":336,"meta":89,"style":89},"#define LOGGER_DEFINE(inst) \\\n   static const struct config = {...}; \\\n   ...\n\n#define LOGGER_DEFINE_WRAPPER(inst) \\\n   IF_ENABLED(VND_TX_IS_COMPILED(DT_INST_PROP(inst, backend)), (LOGGER_DEFINE(inst)))\n\nDT_INST_FOREACH_STATUS_OKAY(LOGGER_DEFINE_WRAPPER)\n",[39,353,354,359,364,369,375,380,385,389],{"__ignoreMap":89},[93,355,356],{"class":95,"line":96},[93,357,358],{},"#define LOGGER_DEFINE(inst) \\\n",[93,360,361],{"class":95,"line":102},[93,362,363],{},"   static const struct config = {...}; \\\n",[93,365,366],{"class":95,"line":108},[93,367,368],{},"   ...\n",[93,370,371],{"class":95,"line":114},[93,372,374],{"emptyLinePlaceholder":373},true,"\n",[93,376,377],{"class":95,"line":120},[93,378,379],{},"#define LOGGER_DEFINE_WRAPPER(inst) \\\n",[93,381,382],{"class":95,"line":126},[93,383,384],{},"   IF_ENABLED(VND_TX_IS_COMPILED(DT_INST_PROP(inst, backend)), (LOGGER_DEFINE(inst)))\n",[93,386,387],{"class":95,"line":132},[93,388,374],{"emptyLinePlaceholder":373},[93,390,391],{"class":95,"line":138},[93,392,393],{},"DT_INST_FOREACH_STATUS_OKAY(LOGGER_DEFINE_WRAPPER)\n",[11,395,396,397,399,400,402],{},"This results in our desired outcome of each logger node only existing in the build if the backend is compiled in. So now we have a way to control features at the Kconfig level (e.g. ",[39,398,221],{},", ",[39,401,233],{},") and have our devices pop into existence when their dependencies are met - without needing board specific application overlays! (happy times ahead)",[404,405,407],"h3",{"id":406},"additional-suggestion-when-you-have-a-per-interface-compatible","Additional suggestion when you have a per-interface compatible...",[11,409,410,411,413],{},"If each of your ",[39,412,252],{}," devices have an additional per-interface \"compatible\" then dependency information can be moved out to the devicetree binding like so:",[84,415,417],{"className":86,"code":416,"language":88,"meta":89,"style":89},"tx_udp {\n    compatible = \"vnd,tx-udp\", \"vnd,tx\";\n    status = \"okay\";\n};\n",[39,418,419,423,428,432],{"__ignoreMap":89},[93,420,421],{"class":95,"line":96},[93,422,105],{},[93,424,425],{"class":95,"line":102},[93,426,427],{},"    compatible = \"vnd,tx-udp\", \"vnd,tx\";\n",[93,429,430],{"class":95,"line":108},[93,431,117],{},[93,433,434],{"class":95,"line":114},[93,435,123],{},[11,437,438],{},[55,439,440],{},"vnd,tx-udp.yaml:",[84,442,446],{"className":443,"code":444,"language":445,"meta":89,"style":89},"language-yaml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","compatible: 'vnd,tx-udp'\nproperties:\n  depends-on:\n    type: string\n    default: 'CONFIG_VND_TX_UDP'\n","yaml",[39,447,448,467,475,482,492],{"__ignoreMap":89},[93,449,450,454,457,460,464],{"class":95,"line":96},[93,451,453],{"class":452},"swJcz","compatible",[93,455,58],{"class":456},"sMK4o",[93,458,459],{"class":456}," '",[93,461,463],{"class":462},"sfazB","vnd,tx-udp",[93,465,466],{"class":456},"'\n",[93,468,469,472],{"class":95,"line":102},[93,470,471],{"class":452},"properties",[93,473,474],{"class":456},":\n",[93,476,477,480],{"class":95,"line":108},[93,478,479],{"class":452},"  depends-on",[93,481,474],{"class":456},[93,483,484,487,489],{"class":95,"line":114},[93,485,486],{"class":452},"    type",[93,488,58],{"class":456},[93,490,491],{"class":462}," string\n",[93,493,494,497,499,501,503],{"class":95,"line":120},[93,495,496],{"class":452},"    default",[93,498,58],{"class":456},[93,500,459],{"class":456},[93,502,225],{"class":462},[93,504,466],{"class":456},[11,506,507],{},"This means the dependency information can be defined in a single location, instead of on each board.",[509,510,511],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}",{"title":89,"searchDepth":102,"depth":102,"links":513},[514],{"id":406,"depth":108,"text":407},"2024-07-03","Reduce the need to develop board-specific overlay files when wanting to implement common drivers by using a helpful macro.","md",{},"\u002Fdevelopers\u002Fnotes\u002Fzephyr-kconfig-controlled-devicetree-instances",{"title":5,"description":516},"developers\u002Fnotes\u002Fzephyr-kconfig-controlled-devicetree-instances","PEHZMWnT_IcjivTGIX-Vj7aEsvGeFzqS8GvbwArORJs",1790559262444]