Open RF Prototyping

Using Linux UIO on the Red Pitaya Board

The Example PL Design

Pavel Demin's red-pitaya-notes AXI Status and Configuration registers test design is used to implement simple UIO driver instances. Figure 1 shows the block design.

UIO blk design

Block design used for the UIO example.

Modifications to the kernel device tree

The following modifications must be made to the generated pl.dtsi device tree file. This patch is contained in the file pldtsi.patch which is placed in the sts_test project directory (red-pitaya-notes/projects/sts_test).

--- pl.dtsi.orig    2021-08-22 00:21:32.216080707 +0000
+++ pl.dtsi 2021-08-22 00:22:06.597866450 +0000
@@ -14,7 +14,7 @@
                cfg_0: axi_cfg_register@40000000 {
                        clock-names = "aclk";
                        clocks = <&misc_clk_0>;
-                   compatible = "xlnx,axi-cfg-register-1.0";
+                   compatible = "generic-uio";
                        reg = <0x40000000 0x1000>;
                };
                misc_clk_0: misc_clk_0 {
@@ -25,7 +25,7 @@
                sts_0: axi_sts_register@40001000 {
                        clock-names = "aclk";
                        clocks = <&misc_clk_0>;
-                   compatible = "xlnx,axi-sts-register-1.0";
+                   compatible = "generic-uio";
                        reg = <0x40001000 0x1000>;
                };
        };

A small modification the red-pitaya-notes top level Makefile then applies the patch as part of the build:

tmp/%.tree/system-top.dts: tmp/%.xsa $(DTREE_DIR)
        mkdir -p $(@D)
        $(XSCT) scripts/devicetree.tcl $* $(PROC) $(DTREE_DIR)
        sed -i 's|#include|/include/|' $@
        patch -d $(@D) < patches/devicetree.patch
ifneq (,$(wildcard projects/$(NAME)/pldtsi.patch))
        patch $(@D)/pl.dtsi projects/$(NAME)/pldtsi.patch
endif

Building the design

The design is built using the red-pitaya-notes top level makefile:

make NAME=sts_test

The output products can then be copied into place inside the TFTP server staging area:

rp-dev2:/var/lib/tftp$ cp ~/Source/red-pitaya-notes/uImage sts_test
rp-dev2:/var/lib/tftp$ cp ~/Source/red-pitaya-notes/devicetree.dtb sts_test.dtb
rp-dev2:/var/lib/tftp$ cp ~/Source/red-pitaya-notes/tmp/sts_test.bit .
rp-dev2:/var/lib/tftp$ rm devicetree.dtb
rp-dev2:/var/lib/tftp$ ln -s sts_test.dtb devicetree.dtb
rp-dev2:/var/lib/tftp$ rm fpga.bit
rp-dev2:/var/lib/tftp$ ln -s sts_test.bit fpga.bit
rp-dev2:/var/lib/tftp$ rm uImage
rp-dev2:/var/lib/tftp$ ln -s sts_test uImage

Additional bootargs parameter

The following parameter specification must be added to the U-boot bootargs in order for the kernel to load and enable the generic-uio driver:

uio_pdrv_genirq.of_id=generic-uio

The Running System

During kernel boot the following lines are output:

uio_pdrv_genirq 40000000.axi_cfg_register: IRQ index 0 not found
uio_pdrv_genirq 40001000.axi_sts_register: IRQ index 0 not found

indicating that the generic-uio driver is active and that no interrupt sources are defined.

There are now two char devices available in /sys/class/uio and /dev:

# ls -l /sys/class/uio/
total 0
lrwxrwxrwx 1 root root 0 Aug 22 11:17 uio0 -> ../../devices/soc0/amba_pl/40000000.axi_cfg_register/uio/uio0
lrwxrwxrwx 1 root root 0 Aug 22 11:17 uio1 -> ../../devices/soc0/amba_pl/40001000.axi_sts_register/uio/uio1
# ls -l /sys/class/uio/uio0/
total 0
-r--r--r-- 1 root root 4096 Aug 22 11:18 dev
lrwxrwxrwx 1 root root    0 Aug 22 11:18 device -> ../../../40000000.axi_cfg_register
-r--r--r-- 1 root root 4096 Aug 22 11:18 event
drwxr-xr-x 3 root root    0 Aug 22 11:18 maps
-r--r--r-- 1 root root 4096 Aug 22 11:18 name
drwxr-xr-x 2 root root    0 Aug 22 11:18 power
lrwxrwxrwx 1 root root    0 Aug 22 11:17 subsystem -> ../../../../../../class/uio
-rw-r--r-- 1 root root 4096 Aug 22 11:17 uevent
-r--r--r-- 1 root root 4096 Aug 22 11:18 version
# ls -l /sys/class/uio/uio1/
total 0
-r--r--r-- 1 root root 4096 Aug 22 11:18 dev
lrwxrwxrwx 1 root root    0 Aug 22 11:18 device -> ../../../40001000.axi_sts_register
-r--r--r-- 1 root root 4096 Aug 22 11:18 event
drwxr-xr-x 3 root root    0 Aug 22 11:18 maps
-r--r--r-- 1 root root 4096 Aug 22 11:18 name
drwxr-xr-x 2 root root    0 Aug 22 11:18 power
lrwxrwxrwx 1 root root    0 Aug 22 11:17 subsystem -> ../../../../../../class/uio
-rw-r--r-- 1 root root 4096 Aug 22 11:17 uevent
-r--r--r-- 1 root root 4096 Aug 22 11:18 version
# ls -l /dev/uio*
crw------- 1 root root 245, 0 Aug 22 11:17 /dev/uio0
crw------- 1 root root 245, 1 Aug 22 11:17 /dev/uio1

Accessing the devices

Memory regions associated with the devices may now be accessed by using mmap. Note that the devices must be opened with the O_RDWR flag.

$ python
Python 3.8.5 (default, Aug 15 2021, 17:59:10)
[GCC 6.3.0 20170516] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from pathlib import Path
>>> import os
>>> from os import O_RDWR
>>> from mmap import mmap, PROT_READ, PROT_WRITE
>>> cfg_path = Path('/sys/class/uio/uio0')
>>> size_path = cfg_path / 'maps/map0/size'
>>> cfg_dev = '/dev/uio0'
>>> with open(size_path) as size_fd:
...     cfg_len = int(size_fd.read(), 16)
...
>>> cfg_fd = os.open(cfg_dev, O_RDWR)
>>> cfg_mem = mmap(cfg_fd, cfg_len)
>>> cfg_mem[0]
0
>>> cfg_mem[0:10]
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
>>> # The design drives seven of the on boards LEDs using configuration
>>> # bits 128 to 134
>>> 128/8
16.0
>>> cfg_mem[16] = 0x1
>>> cfg_mem[16] = 0x0
>>> cfg_mem[16] = 0x2
>>> cfg_mem[16] = 0x4
>>> sts_path = Path('/sys/class/uio/uio1')
>>> size_path = sts_path / 'maps/map0/size'
>>> sts_dev = '/dev/uio1'
>>> with open(size_path) as size_fd:
...     sts_len = int(size_fd.read(), 16)
...
>>> sts_fd = os.open(sts_dev, O_RDWR)
>>> sts_mem = mmap(sts_fd, sts_len, flags=PROT_READ)
>>> sts_mem[16]
4
>>>

In the code above, writing values to cfg_mem[16] will toggle the on board LEDs. Likewise, reading sts_mem[16] will return the current status of the on board LEDs.

The following code cleans up:

>>> sts_mem.close()
>>> cfg_mem.close()
>>> os.close(sts_fd)
>>> os.close(cfg_fd)