# 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%
```