Adding UART to Linux Yocto
In this example you will add single UART Lite from FPGA resources and connect it to PMOD connector of Zybo board.
Procedure
- Add new hardware with SPI in Vivado and generate XSA file.
- Add new XSA file in Yocto.
- Configure Linux kernel to use UARTLITE.
Results
Finally, after these steps you will have in Yocto:
- add single UART Lite in PL connected to AXI and externally to PMOD JC pins 1 (TX) and 2 (RX).
1. In Vivado: add new SPI hardware to obtain XSA file
Add AXI Uartlite in FPGA
- Add AXI Uartlite IP core
- Run Connection Automation
- Remove the UART external connection, click + near Uartlite UART port to see rx and tx lines, then make rx and tx as External and name them as uart0_rx and uart0_tx. Alternatively, you can leave the connections, generate wrapper and see the port names in the wrapper's HDL code.
- Set UART's baud rate: 115200 bps
- 8 Data Bits
- No Parity
- Connect interrupt:
- Enable interrupts in Zynq (in not already enabled): Zynq/Interrupts/Fabric interrupts/PL-PS Interrupt Ports/IRQ_F2P[15:0].
- Connect AXI Uartlite interrupt pin to Zynq IRQ_F2P input. If you already have something connected to Zynq's interrupt input (i.e. SPI), then add Concat IP block and use it to join interrupts from various sources. If you already have Concat block, increment its input size, then connect interrupt by dragging a wire connection with the mouse.
Edit constrains
Example of XDC (watch out of pin assignments, adjust to your needs!):
##Pmod Header JC
set_property -dict { PACKAGE_PIN T11 IOSTANDARD LVCMOS33 } [get_ports { uart0_rx }]; #IO_L1P_T0_34 Sch=jc_p[2] JC-3
set_property -dict { PACKAGE_PIN T10 IOSTANDARD LVCMOS33 } [get_ports { uart0_tx }]; #IO_L1N_T0_34 Sch=jc_n[2] JC-4
Generate and export
- Generate bitstream
- Export hardware with bitstream to XSA file
2. Configure Linux Yocto - add changed XSA to Yocto
- Copy newly generated XSA to:
lab_msw_spi/yocto-scarthgap/poky/meta-custom-zybo-z7/recipes-bsp/external-hdf/files/<your_XSA_FILE>
- Edit the file: lab_msw_spi/yocto-scarthgap/poky/meta-custom-zybo-z7/recipes-bsp/external-hdf/external-hdf_%.bbappend
and update the XSA filename (if changed after adding SPI hardware) :
FILESEXTRAPATHS:prepend := "${THISDIR}/files:"
HDF_EXT = "xsa"
HDF_URI = file://zybo-z7-20-hw-platform_spi_uart.xsa
3. Configure Linux Yocto - enable CONFIG_SERIAL_UARTLITE in the Yocto kernel
Find the name for the recipe
- Find the name of virtual provider for the kernel in your project:
bitbake -e core-image-minimal-dev | grep ^PREFERRED_PROVIDER_virtual/kernel= à PREFERRED_PROVIDER_virtual/kernel="linux-xlnx"
- Use the name of the provider (here linux-xlnx) to find the name of the kernel recipe used in your project:
bitbake -e linux-xlnx | grep ^FILE= à FILE="/home2/ldap/Users/wujek_ldap/lab_msw_spi/yocto-scarthgap/poky/meta-xilinx/meta-xilinx-core/recipes-kernel/linux/linux-xlnx_6.1-v2023.2.bb"
- Use the name of the recipe (here linux-xlnx_6.1-v2023.2.bb) to create BBAPPEND file to include it in your kernel recipe.
Create BBAPPEND file
- Create file with the name of the recipe found above:
nano recipes-kernel/linux/linux-xlnx_6.1-v2023.2.bbappend
with lines (if you are not using SPI, remove parts for spi.cfg):
# Look for files (in folder recipes-kernel/linux/)
FILESEXTRAPATHS:prepend := "${THISDIR}:"
SRC_URI:append = " \
file://spidev.cfg \
file://uartlite.cfg \
"
do_configure:append() {
bbnote "Merging custom kernel config fragments (spidev + uartlite)"
KCONFIG_CONFIG="${B}/.config" \
bash ${S}/scripts/kconfig/merge_config.sh -m -O ${B} \
${B}/.config ${WORKDIR}/spidev.cfg ${WORKDIR}/uartlite.cfg oe_runmake -C ${S} O=${B} olddefconfig
}
The function do_configure:append() injects uartlite.cfg after the XSCT tools from Vivado builds the kernel for Zybo and sets Zybo defaults. The function provides, that the setting CONFIG_SERIAL_UARTLITE=y, CONFIG_SERIAL_UARTLITE_CONSOLE=y will be applied at the end and will not be overwritten by defaults.
Create kernel configuration fragment file
- Create file:
nano recipes-kernel/linux/linux-xlnx/uartlite.cfg
with line:
CONFIG_SERIAL_UARTLITE=y CONFIG_SERIAL_UARTLITE_CONSOLE=y
Final check
You should have the following files:
meta-custom-zybo-z7/
├── conf
│ ├── layer.conf
│ └── machine
│ └── zybo-z7.conf
├── COPYING.MIT
├── README
├── recipes-bsp
│ ├── bootfiles
│ │ ├── bootfiles.bb
│ │ └── files
│ │ ├── boot.cmd
│ │ └── uEnv.txt
│ ├── device-tree
│ │ ├── device-tree.bbappend
│ │ └── files
│ │ └── system-user.dtsi
│ └── external-hdf
│ ├── external-hdf_%.bbappend
│ └── files
│ └── zybo-z7-20-hw-platform_spi_uart.xsa (updated XSA)
├── recipes-core
│ └── base-files
│ | └── base-files_%.bbappend
├── recipes-kernel (new)
│ ├── linux (new)
│ │ ├── linux-xlnx_6.1-v2023.2.bbappend ß(added some lines)
│ │ └── uartlite.cfg ß(added CONFIG_SERIAL_UARTLITE_*)
└── wks
└── wic-zynq.wks
meta-custom-zybo-z7/
└── recipes-kernel/
└── linux/
├── linux-xlnx_6.1-v2023.2.bbappend
└── spidev.cfg
Check if setting will work (replace ~/yocto_tmp with your $TMPDIR):
bitbake -c cleansstate linux-xlnx bitbake -c configure linux-xlnx grep CONFIG_SERIAL_UARTLITE $(find ~/yocto_tmp/work -type f -name ".config" -path "*/linux-xlnx/*");
You should get from the grep:
CONFIG_SERIAL_UARTLITE=y CONFIG_SERIAL_UARTLITE_CONSOLE=y CONFIG_SERIAL_UARTLITE_NR_UARTS=16
4. Configure Linux Yocto - correct the order of UART ports in Device Tree
- Build the system and decode your current device tree:
cp ~/yocto_tmp/deploy/images/zybo-z7/devicetree/system-top.dtb . dtc -I dtb -O dts system-top.dtb > system-top.dts
Adding new UART, assigned the Linux console to different number, you can see your current Device Tree:
chosen {
bootargs = "earlycon";
stdout-path = "serial0:115200n8";
};
aliases {
ethernet0 = "/axi/ethernet@e000b000";
serial0 = "/amba_pl/serial@42c00000";
serial1 = "/axi/serial@e0001000";
spi0 = "/axi/spi@e000d000";
spi1 = "/axi/spi@e0006000";
spi2 = "/axi/spi@e0007000";
spi3 = "/amba_pl/axi_quad_spi@41e00000";
};
Basing on your Device Tree, change the PS console again to serial0 by adding at the end of file:
nano poky/meta-custom-zybo-z7/recipes-bsp/device-tree/files/system-user.dtsi
this reversed info (also repeat the chosen section):
/ {
aliases {
serial0 = "/axi/serial@e0001000"; /* PS UART1 */
serial1 = "/amba_pl/serial@42c00000"; /* AXI UartLite */
};
chosen {
stdout-path = "serial0:115200n8";
bootargs = "console=ttyPS1,115200 earlycon";
};
};
- Rebuild project:
bitbake core-image-minimal-dev
5. Test new UART
- Connect TX and RX pins of your UART. In this example, connect pin 3 and pin 4 of PMOD JC.
- Run microcom:
microcom -s 115200 /dev/ttyUL0
- Type any text, you should see it on the terminal. If you remove the wired connection, you will stop seeing the text on the terminal.




