# Zephyr RTOS - Fixing `pinctrl` for Samples on STM32 [TOC] ## Prelude I was compiling [USB CDC ACM Sample Composite Application](https://docs.zephyrproject.org/latest/samples/subsys/usb/cdc_acm_composite/README.html) on my STM32F411RE: ``` $ west build -p -b nucleo_f411re ``` And the smaple somehow doesn't compile: ```diff -- Found devicetree overlay: /Users/0xff19/zephyrproject/zephyr/samples/subsys/usb/cdc_acm_composite/app.overlay devicetree error: /Users/0xff19/zephyrproject/zephyr/samples/subsys/usb/cdc_acm_composite/app.overlay:7 (column 1): parse error: undefined node label 'zephyr_udc0' -- In: /Users/0xff19/zephyrproject/zephyr/samples/subsys/usb/cdc_acm_composite/build/zephyr, command: /Users/0xff19/zephyrproject/.venv/bin/python3.9;/Users/0xff19/zephyrproject/zephyr/scripts/dts/gen_defines.py;--dts;/Users/0xff19/zephyrproject/zephyr/samples/subsys/usb/cdc_acm_composite/build/zephyr/zephyr.dts.pre;--dtc-flags;'';--bindings-dirs;/Users/0xff19/zephyrproject/zephyr/dts/bindings;--header-out;/Users/0xff19/zephyrproject/zephyr/samples/subsys/usb/cdc_acm_composite/build/zephyr/include/generated/devicetree_generated.h.new;--dts-out;/Users/0xff19/zephyrproject/zephyr/samples/subsys/usb/cdc_acm_composite/build/zephyr/zephyr.dts.new;--edt-pickle-out;/Users/0xff19/zephyrproject/zephyr/samples/subsys/usb/cdc_acm_composite/build/zephyr/edt.pickle;--vendor-prefixes;/Users/0xff19/zephyrproject/zephyr/dts/bindings/vendor-prefixes.txt CMake Error at /Users/0xff19/zephyrproject/zephyr/cmake/modules/dts.cmake:231 (message): gen_defines.py failed with return code: 1 Call Stack (most recent call first): /Users/0xff19/zephyrproject/zephyr/cmake/modules/zephyr_default.cmake:113 (include) /Users/0xff19/zephyrproject/zephyr/share/zephyr-package/cmake/ZephyrConfig.cmake:66 (include) /Users/0xff19/zephyrproject/zephyr/share/zephyr-package/cmake/ZephyrConfig.cmake:92 (include_boilerplate) CMakeLists.txt:4 (find_package) -- Configuring incomplete, errors occurred! FATAL ERROR: command exited with status 1: /opt/homebrew/bin/cmake -DWEST_PYTHON=/Users/0xff19/zephyrproject/.venv/bin/python3 -B/Users/0xff19/zephyrproject/zephyr/samples/subsys/usb/cdc_acm_composite/build -GNinja -DBOARD=nucleo_f411re -S/Users/0xff19/zephyrproject/zephyr/samples/subsys/usb/cdc_acm_composite ``` However, hope is not all lost. It does compile on some targets, say, `stm32f4_disco`: ``` $ west build -p -b stm32f4_disco ``` This happens quite often in Zephyr samples. Samples are just for demonstrating purpose, showing how certain mechanisms work on a specific piece of hardware. Very often a sample working on one board will not work directly on another board --- even if they are from the same vendor! Let's try to make this sample work on NUCELO-F411RE. ## Problem Analysis The error message emitted during build for `nucleo_f411re` gives away some clues. It says: ``` devicetree error: ... /zephyrproject/zephyr/samples/subsys/usb/cdc_acm_composite/app.overlay:7 (column 1): parse error: undefined node label 'zephyr_udc0' ``` We can find that for DISCO, `zephyr_udc0` label is defined in [`boards/arm/stm32f4_disco/stm32f4_disco.dts`](https://github.com/zephyrproject-rtos/zephyr/blob/main/boards/arm/stm32f4_disco/stm32f4_disco.dts#L121), which is a part of board definition of DISCO. NUCLEO on the contrary doesn't have this, at least not in its board definition. There are more hints inside full devicetree after build for `stm32f4_disco`: ``` $ vim -R build/zephyr/zephyr.dts ``` `usbotg_fs` can be found under root node: ```c usbotg_fs: zephyr_udc0: usb@50000000 { compatible = "st,stm32-otgfs"; reg = < 0x50000000 0x40000 >; interrupts = < 0x43 0x0 >; interrupt-names = "otgfs"; num-bidir-endpoints = < 0x4 >; ram-size = < 0x500 >; maximum-speed = "full-speed"; phys = < &otgfs_phy >; clocks = < &rcc 0x34 0x80 >, < &rcc 0x2 0xff >; status = "okay"; pinctrl-0 = < &usb_otg_fs_dm_pa11 &usb_otg_fs_dp_pa12 >; pinctrl-names = "default"; cdc_acm_uart0 { compatible = "zephyr,cdc-acm-uart"; }; cdc_acm_uart1 { compatible = "zephyr,cdc-acm-uart"; }; }; ``` Definition of `usbotg_fs` is located at [`zephyr/dts/arm/st/f4/stm32f4.dtsi`](https://github.com/zephyrproject-rtos/zephyr/blob/main/dts/arm/st/f4/stm32f4.dtsi#L276), which ends up being included by both `stm32f407Xg.dtsi` and `stm32f411Xe.dtsi`. It seems that no reason this sample only works on STM32F407-DISCO but not NUCLEO-F411RE if both board contain `usborg_fs` node, and thus similar capability in terms of USB OTG. Indeed, looking at description in [ST Nucleo F411RE](https://docs.zephyrproject.org/latest/boards/arm/nucleo_f411re/doc/index.html) on Zephyr official documentation, you can find out *USB 2.0 OTG FS* in [Hardware](https://docs.zephyrproject.org/latest/boards/arm/nucleo_f411re/doc/index.html#hardware) section. What's missing here? Observe that for STM32F4-DISCO `zephyr_udc0` is just a label for `usbotg_fs` node, which is defined in devicetre for both boards. Not only can we verify this in full devicetree of DISCO, board definition in [`boards/arm/stm32f4_disco/stm32f4_disco.dts`](https://github.com/zephyrproject-rtos/zephyr/blob/main/boards/arm/stm32f4_disco/stm32f4_disco.dts#L121) also suggests this --- it literally references the `usbotg_fs` node. Chances that all it required is to overlay another `zephyr_udc0` node referencing `usbotg_fs` in NUCLEO devicetree, with similiar properties ## Fix 1: Missing `zephyr_udc0` Label First let's try fixing the missing `zephyr_udc0` label. Since it's just a label referencing `usbotg_fs`, we can try to mimic devicetree of STM32F4-DISCO, adding `zephyr_udc0` label for `usbotg_fs` node in NUCLEO-F411RE board. ### Step 1: Modofiy `app.overlay` This can be done by adding extra nodes in `app.overlay`, the app-specfic devicetree overlay. Modify the `app.overlay` file in directory to this: ```c zephyr_udc0: &usbotg_fs { status = "okay"; }; &zephyr_udc0 { cdc_acm_uart0 { compatible = "zephyr,cdc-acm-uart"; }; cdc_acm_uart1 { compatible = "zephyr,cdc-acm-uart"; }; }; ``` What it does is simply enabling `usbotg_fs` node, and label it `zephyr_udc0` . ### Step 2: Build it! Try to build again: ```bash $ west build -p -b nucleo_f411re ``` Does it build? Not really: ```diff -- west build: making build dir /Users/0xff19/zephyrproject/zephyr/samples/subsys/usb/cdc_acm_composite/build pristine -- west build: generating a build system Loading Zephyr default modules (Zephyr base). -- Application: /Users/0xff19/zephyrproject/zephyr/samples/subsys/usb/cdc_acm_composite -- CMake version: 3.25.2 -- Found Python3: /Users/0xff19/zephyrproject/.venv/bin/python3.9 (found suitable exact version "3.9.7") found components: Interpreter -- Cache files will be written to: /Users/0xff19/Library/Caches/zephyr -- Zephyr version: 3.2.99 (/Users/0xff19/zephyrproject/zephyr) -- Found west (found suitable version "0.14.0", minimum required is "0.7.1") -- Board: nucleo_f411re -- ZEPHYR_TOOLCHAIN_VARIANT not set, trying to locate Zephyr SDK -- Found host-tools: zephyr 0.15.2 (/Users/0xff19/zephyr-sdk-0.15.2) -- Found toolchain: zephyr 0.15.2 (/Users/0xff19/zephyr-sdk-0.15.2) -- Found Dtc: /opt/homebrew/bin/dtc (found suitable version "1.6.1", minimum required is "1.4.6") -- Found BOARD.dts: /Users/0xff19/zephyrproject/zephyr/boards/arm/nucleo_f411re/nucleo_f411re.dts -- Found devicetree overlay: /Users/0xff19/zephyrproject/zephyr/samples/subsys/usb/cdc_acm_composite/app.overlay devicetree error: 'pinctrl-0' is marked as required in 'properties:' in /Users/0xff19/zephyrproject/zephyr/dts/bindings/usb/st,stm32-otgfs.yaml, but does not appear in <Node /soc/usb@50000000 in '/Users/0xff19/zephyrproject/zephyr/misc/empty_file.c'> ``` Good news is that build system no longer complains about missing `zephyr_udc0` node! However, it complains another thing insead --- missing node property, in the last line of error message: ```c devicetree error: 'pinctrl-0' is marked as required in 'properties:' in /Users/0xff19/zephyrproject/zephyr/dts/bindings/usb/st,stm32-otgfs.yaml, but does not appear in <Node /soc/usb@50000000 in '/Users/0xff19/zephyrproject/zephyr/misc/empty_file.c'> ``` ## Fix 2: Filling `pinctrl` property Let's lookup `usbotg_fs` node again on devicetree for `stm32f4_disco`, notably the `pinctrl-0` property. This more or less shed light on what suppose to be provided for our own `pinctrl-0`: ```diff usbotg_fs: zephyr_udc0: usb@50000000 { compatible = "st,stm32-otgfs"; reg = < 0x50000000 0x40000 >; interrupts = < 0x43 0x0 >; interrupt-names = "otgfs"; num-bidir-endpoints = < 0x4 >; ram-size = < 0x500 >; maximum-speed = "full-speed"; phys = < &otgfs_phy >; clocks = < &rcc 0x34 0x80 >, < &rcc 0x2 0xff >; status = "okay"; + pinctrl-0 = < &usb_otg_fs_dm_pa11 &usb_otg_fs_dp_pa12 >; pinctrl-names = "default"; cdc_acm_uart0 { compatible = "zephyr,cdc-acm-uart"; }; cdc_acm_uart1 { compatible = "zephyr,cdc-acm-uart"; }; }; ``` The goal now is to figure out a way to provide `pinctrl-0` in our overlay. We need to find out where nodes like `usb_otg_fs_dm_pa11` and `usb_otg_fs_dp_pa12` are defined to better understand on how to use it. At first thought, you may try to look up `&usb_otg_fs_dm_pa11` and `&usb_otg_fs_dp_pa12` in whatever ending up being included in board devicetree of `nucleo_f411re`. Spoiler alert: It turns out that these pinctrl nodes are no where to be found in Zephyr repository! ### Step 1: Look up pinctrl node For some reason, pinctrl node definitions for stm32 actually locates at a separate repository called [`HAL_stm32`](https://github.com/zephyrproject-rtos/hal_stm32). Notably, in [`hal_stm32/dts/st/f4/stm32f411r(c-e)tx-pinctrl.dtsi`](https://github.com/zephyrproject-rtos/hal_stm32/blob/main/dts/st/f4/stm32f411r(c-e)tx-pinctrl.dtsi#L1032) in this case. `pinctrl` to Zephyr is like the `.ioc` for stm32cube ecosystem. It is used to assign functionalities to a certain pin. Usage of these node is quite self-explanatory from their labels. For example `i2c1_sda_pb9` in the [`/* I2C SDA */`](https://github.com/zephyrproject-rtos/hal_stm32/blob/main/dts/st/f4/stm32f411r(c-e)tx-pinctrl.dtsi#L307) part describe a configuration for PB9 to be used as SDA for `i2c1`. In [`/* USB OTG */`](https://github.com/zephyrproject-rtos/hal_stm32/blob/main/dts/st/f4/stm32f411r(c-e)tx-pinctrl.dtsi#L1032) part we find pinctrl needed, `usb_otg_fs_dm_pa11` and `usb_otg_fs_dp_pa12`, which is exactly the same as that in `stm32f4_disco`. ### Step 2: Reference pinctrl nodes in overlay Add `otg_fs` related pinctrl to the node: ```c zephyr_udc0: &usbotg_fs { pinctrl-0 = <&usb_otg_fs_dm_pa11 &usb_otg_fs_dp_pa12>; status = "okay"; }; &zephyr_udc0 { cdc_acm_uart0 { compatible = "zephyr,cdc-acm-uart"; }; cdc_acm_uart1 { compatible = "zephyr,cdc-acm-uart"; }; }; ``` ### Step 3: Build Again! Let's try to build it again: ```bash $ west build -p -b nucleo_f411re ``` Does it build? Still doesn't. ```diff -- west build: making build dir /Users/0xff19/zephyrproject/zephyr/samples/subsys/usb/cdc_acm_composite/build pristine -- west build: generating a build system Loading Zephyr default modules (Zephyr base). -- Application: /Users/0xff19/zephyrproject/zephyr/samples/subsys/usb/cdc_acm_composite -- CMake version: 3.25.2 -- Found Python3: /Users/0xff19/zephyrproject/.venv/bin/python3.9 (found suitable exact version "3.9.7") found components: Interpreter -- Cache files will be written to: /Users/0xff19/Library/Caches/zephyr -- Zephyr version: 3.2.99 (/Users/0xff19/zephyrproject/zephyr) -- Found west (found suitable version "0.14.0", minimum required is "0.7.1") -- Board: nucleo_f411re -- ZEPHYR_TOOLCHAIN_VARIANT not set, trying to locate Zephyr SDK -- Found host-tools: zephyr 0.15.2 (/Users/0xff19/zephyr-sdk-0.15.2) -- Found toolchain: zephyr 0.15.2 (/Users/0xff19/zephyr-sdk-0.15.2) -- Found Dtc: /opt/homebrew/bin/dtc (found suitable version "1.6.1", minimum required is "1.4.6") -- Found BOARD.dts: /Users/0xff19/zephyrproject/zephyr/boards/arm/nucleo_f411re/nucleo_f411re.dts -- Found devicetree overlay: /Users/0xff19/zephyrproject/zephyr/samples/subsys/usb/cdc_acm_composite/app.overlay devicetree error: 'pinctrl-names' is marked as required in 'properties:' in /Users/0xff19/zephyrproject/zephyr/dts/bindings/usb/st,stm32-otgfs.yaml, but does not appear in <Node /soc/usb@50000000 in '/Users/0xff19/zephyrproject/zephyr/misc/empty_file.c'> ``` Despite being somewhat tideous, it is actually complaining another property missing: ```c devicetree error: 'pinctrl-names' is marked as required in 'properties:' in /Users/0xff19/zephyrproject/zephyr/dts/bindings/usb/st,stm32-otgfs.yaml, but does not appear in <Node /soc/usb@50000000 in '/Users/0xff19/zephyrproject/zephyr/misc/empty_file.c'> ``` ## Fix 3: `pinctrl-names` According to devicetree binding documentation for `pinctrl-names` in [`dts/bindings/pinctrl/pinctrl-device.yaml` ](https://github.com/zephyrproject-rtos/zephyr/blob/main/dts/bindings/pinctrl/pinctrl-device.yaml#L39), it is an array of string whose sequence must match the numbering of `pinctrl-n`. First string in `pinctrl-names` indicates the name of `pinctrl-0`; the second one being the name of `pinctrl-1` and so on. ### Step 1: Adding `pinctrl-names` Since there's only `pinctrl-0`, only a string is needed in this preoperty. Let's call it `default` and add it to the `app.overlay`: ```c zephyr_udc0: &usbotg_fs { pinctrl-0 = <&usb_otg_fs_dm_pa11 &usb_otg_fs_dp_pa12>; pinctrl-names = "default"; status = "okay"; }; &zephyr_udc0 { cdc_acm_uart0 { compatible = "zephyr,cdc-acm-uart"; }; cdc_acm_uart1 { compatible = "zephyr,cdc-acm-uart"; }; }; ``` ### Step 2: Third Time's the Charm Build it again: ```bash $ west build -p -b nucleo_f411re ``` Viola! now it build successfully: ```c -- west build: building application [1/178] Preparing syscall dependency handling [3/178] Generating include/generated/version.h -- Zephyr version: 3.2.99 (/Users/0xff19/zephyrproject/zephyr), build: zephyr-v3.2.0-3954-g1480b2083e4a [168/178] Linking C executable zephyr/zephyr_pre0.elf [172/178] Linking C executable zephyr/zephyr_pre1.elf [178/178] Linking C executable zephyr/zephyr.elf Memory region Used Size Region Size %age Used FLASH: 47400 B 512 KB 9.04% RAM: 14656 B 128 KB 11.18% IDT_LIST: 0 GB 2 KB 0.00% ```