In this simple tutorial we will discuss how to build a Linux image in Petalinux 2018.3, from the BSP, or from scratch using the HDF file that is export from Vivado IP Integrator. The wiki will also discuss key points such as the
on board clocks and these can be programmed via Linux script or via a C application on the A53, that the user can program these to suit their own design needs. The wiki will also discuss the various debug options available to enable the user to fully debug any application targeting the RFDC IP driver.
Building the Linux Image:
Using BSP:
petalinux-create -t project -s <path to BSP> cd <plnx proj dir> petalinux-config -c rootfs base -> [*] i2c-tools petalinux-build --sdk petalinux-package --sysroot
Using zynqMP template:
petalinux-create -t project --template zynqMP -n rfdc_linux cd rfdc_linux petalinux-config --get-hw-description=<path to HDF> DTG Settings -> (zcu111-reva) MACHINE_NAME Open rfdc_linux\project-spec\meta-user\recipes-core\images\petalinux-image-full.bbappend IMAGE_INSTALL_append = " rfdc" IMAGE_INSTALL_append = " rfdc-read-write" IMAGE_INSTALL_append = " rfdc-selftest" petalinux-config -c rootfs base -> [*] i2c-tools user packages -> [*] rfdc [*] rfdc-read-write [*] rfdc-selftest Add the rfdc-examples folder to project-spec/meta-user/recipes-bsp Create the rfdc-selftest_%.bbappend and add the content shown below here: do_compile (){ make all BOARD_FLAG=-DXPS_BOARD_ZCU111 OUTS=rfdc-selftest RFDC_OBJS=xrfdc_selftest_example.o } petalinux-config Yocto Settings -> (zcu111-zynqmp) YOCTO MACHINE NAME petalinux-build petalinux-package --boot --fpga system.bit --u-boot petalinux-build --sdk petalinux-package --sysroot
petalinux-build --sdk
This steps can take along time ~ 40 mins to complete
The RFDC driver is delivered as a shared object in Linux. The makefile can be seen here and below:
APP = rfdc LIBSOURCES=*.c OUTS = *.o NAME := rfdc MAJOR = 1 MINOR = 1 VERSION = $(MAJOR).$(MINOR) all: lib$(NAME).so lib$(NAME).so.$(VERSION): $(OUTS) $(CC) $(LDFLAGS) $(OUTS) -shared -Wl,-soname,lib$(NAME).so.$(MAJOR) -o lib$(NAME).so.$(VERSION) -lmetal lib$(NAME).so: lib$(NAME).so.$(VERSION) rm -f lib$(NAME).so.$(MAJOR) lib$(NAME).so ln -s lib$(NAME).so.$(VERSION) lib$(NAME).so.$(MAJOR) ln -s lib$(NAME).so.$(MAJOR) lib$(NAME).so %.o: %.c $(CC) $(CFLAGS) -c -fPIC $(LIBSOURCES) clean: rm -rf *.o *.so *.so.*
So, this .so is built and delivered in the /usr folder in the root file system. The recipe can be seen here:
SUMMARY = "rfdc Library" SECTION = "rfdc" LICENSE = "BSD" LIC_FILES_CHKSUM = "file://${WORKDIR}/git/license.txt;md5=71602ce1bc2917a9be07ceee6fab6711" inherit pkgconfig REPO ??= "git://github.com/Xilinx/embeddedsw.git;protocol=https" BRANCH ??= "release-2018.3" SRCREV ??= "56f3da2afbc817988c9a45b0b26a7fef2ac91706" BRANCHARG = "${@['nobranch=1', 'branch=${BRANCH}'][d.getVar('BRANCH', True) != '']}" COMPATIBLE_MACHINE = "^$" COMPATIBLE_MACHINE_zynqmpdr = "zynqmpdr" FILESEXTRAPATHS_prepend := "${THISDIR}/files:" SRC_URI = " \ ${REPO};${BRANCHARG} \ " S = "${WORKDIR}/git/XilinxProcessorIPLib/drivers/rfdc/src/" PACKAGE_ARCH = "${SOC_FAMILY}${SOC_VARIANT}" DEPENDS = "libmetal" PROVIDES = "rfdc" do_configure() { cp ${S}/Makefile.Linux ${S}/Makefile } do_install() { install -d ${D}${libdir} install -d ${D}${includedir} oe_libinstall -so librfdc ${D}${libdir} install -m 0644 ${S}/xrfdc_hw.h ${D}${includedir}/xrfdc_hw.h install -m 0644 ${S}/xrfdc.h ${D}${includedir}/xrfdc.h install -m 0644 ${S}/xrfdc_mts.h ${D}${includedir}/xrfdc_mts.h } FILES_${PN} = "${libdir}/*.so.*" FILES_${PN}-dev = "${libdir}/*.so ${includedir}/*"
We can see this .SO added to the sysroots created above:
These will also be in the respective folder in the rootfs when you boot Linux.
Creating Linux Application in SDK:
The SDK 2018.3 can be used to create the Linux application
File → New → Application Project
sysroots
Point to the Linux System Root that was created using the petalinux-build --sdk step above
Finish, to continue
Right Click on the application in Project Explorer and select the C/C+ Build Settings:
Add the path to the sysroot path to the linker flags:
Add the libraries (highlighted):
If using the ZCU111, then there is code to program the clocks on the board:
As seen above, there is a pre-compiler ifdef. This can be added as a symbol in the C/C++ Build settings too:
In this demo, I will be using the rfdc_selftest that is delivered with the driver. However, the full API suite for the RFDC can be found here.
To add the code, right click on the src folder in Project explorer and select Import:
General → File System:
Browse to your source code and select Finish:
ZCU111 Board Clocks Topology:
The clocking structure on the ZCU111 can be seen on page 54 here. The Clock sources; LMK04208 and LMX2594 are accessible via the I2CSPI over the IC21 bus:
The device-tree node for this can be seen here:
i2c@5 { #address-cells = <1>; #size-cells = <0>; reg = <5>; sc18is603@2f { /* sc18is602 - u93 */ compatible = "nxp,sc18is603"; reg = <0x2f>; /* 4 gpios for CS not handled by driver */ /* * USB2ANY cable or * LMK04208 - u90 or * LMX2594 - u102 or * LMX2594 - u103 or * LMX2594 - u104 */ }; };
As shown in the sc18is603 device node, there are 4 CS for each clock source:
- LE_2494_A (LMX RF1 PLL) = 0x8
- LE_2594_B (LMX RF2 PLL) = 0x4
- LE_4208 (LMK) = 0x2
- LE_2594_C (LMX RF3 PLL) = 0x1
ZCU111 Board Clocks Programming:
There is source code provided in the RFDC driver example; xrfdc_clk.c and xrfdc_clk.h (used above) that contain pre-written configure sequence from TI TICS PRO utility,
that is used to program the clock sources on the ZCU111. Users can also use the i2c-tools utility in Linux to program these clocks. Both flows are discussed below
LMK04208:
This is the low noise jitter cleaner that feds the LMK2594. The LMX2594 is programmed using 24-bit shift registers. The shift register consists of a R/W bit (MSB), followed
by a 7-bit address field and a 16-bit data field. For the R/W bit, 0 is for write, and 1 is for read. The address field
ADDRESS[6:0] is used to decode the internal register address. The remaining 16 bits form the data field DATA[15:0].
LMX2594:
The LMX is programmed using 24 bit shift register. This is the clock provides the clock to the tiles on the RFDC:
LMK04208 C Code Programming:
The LMK04208 can be programmed via a C code application delivered with the RFDC driver, the code snippet is shown below.
#ifdef XPS_BOARD_ZCU111 unsigned int LMK04208_CKin[1][26] = { {0x00160040,0x80140320,0x80140321,0x80140322, 0xC0140023,0x40140024,0x80141E05,0x03300006,0x01300007,0x06010008, 0x55555549,0x9102410A,0x0401100B,0x1B0C006C,0x2302886D,0x0200000E, 0x8000800F,0xC1550410,0x00000058,0x02C9C419,0x8FA8001A,0x10001E1B, 0x0021201C,0x0180033D,0x0200033E,0x003F001F }}; #endif
LMX2594 C Code programming:
The LMK04208 can be programmed via a C code application delivered with the RFDC driver, the code snippet is shown below.
There are pre-configured sequences in the code that will allow the user to use one any of the frequencies below:
5120000 3932160 1474560 4915200 6553600 6400000 6389760 6144000 5898240 5734400 4000000 4096000 4423680 4669440 1966080 2048000 2457600 2949120 3072000 3276800 245760 3686400 204800 409600 491520 737280 7340032
These two functions can be called in the user application as shown below:
LMK04208ClockConfig(12, LMK04208_CKin); LMX2594ClockConfig(12, 3932160);
ZCU111 BOARD
If using the rfdc example code, then the user will need to set the #define XPS_BOARD_ZCU111.
This can be passed as a symbol in C/C++ Build Settings in SDK (see above)
Or, via the (zcu111-zynqmp) YOCTO MACHINE NAME in Petalinux/Yocto
Using i2c-tools to program the LMK and LMX devices:
Users can use the i2cdetect utility to read all the devices on the I2C bus. For example:
Here, the driver will “hop” through each mux to show all possible devices in the i2c bus topology.
In order to program the LMK or LMX devices on the ZCU111, users will need to find the SCI18IS60C device on the I2C bus.
To do this, users can do a i2cdetect -y -r busaddr
Where the busaddr would be 0 to 22 in this case (as per the i2cdetect -l command above).
Users can do a grep on the result to see if the 0x2f (as we seen form the device node above is the SCI18IS60C device).
If the i2c bus topology isnt likely to change, then we can just hard-code this address into the script command.
However, if the bus topology is likely to change, ie add remove i2c from PS or AXI I2C in the PL, then user can script around this to dynamically find the SCI18IS60C device
For example, the function below will search through all possible bus addresses, and do a grep until it finds the expected device ID (0x2f):
fFindSCI18IS602 () { printf "Scanning for SCI18IS602... " for (( ii=0; ii<30; ii++ )); do #ls -l /dev/i2c* | grep i2c-${ii} > /dev/null #if [ $? -eq 0 ]; then if [ -e /dev/i2c-${ii} ]; then # Check for SCI18IS602 #echo "i2c-${ii} bus:" i2cdetect -y -r ${ii} | grep -i $vSCI18IS602Addr > /dev/null if [ $? -eq 0 ]; then vSCI18IS602Bus=${ii} vSCI18IS602Addr="0x${vSCI18IS602Addr}" echo "Detected SCI18IS602 at bus=${vSCI18IS602Bus}, address=${vSCI18IS602Addr}." vSCI18IS602Found=1 fi fi done if [ $vSCI18IS602Found -eq 0 ]; then echo "Warning: SCI18IS602 device not found!" fi }
Now, that the bus address for the SCI18IS602 device is found. we can access the various clocks by passing the CS. The various CS can be seen below:
- LE_2494_A (LMX RF1 PLL) = 0x8
- LE_2594_B (LMX RF2 PLL) = 0x4
- LE_4208 (LMK) = 0x2
- LE_2594_C (LMX RF3 PLL) = 0x1
To target the LMK, then the i2cset command would look like:
i2cset -y 12 0x2f 0x2 <32 bit data> i
To target the LMX, then the i2cset command would look like:
i2cset -y 12 0x2f 0x1 <24 bit data> i i2cset -y 12 0x2f 0x4 <24 bit data> i i2cset -y 12 0x2f 0x8 <24 bit data> i
The data can be created using the TICS Pro tool from TI. This script can be passed to the Petalinux project as shown below:
petalinux-create --type apps --template install --name bootscript –enable
update the project-spec\meta-user\recipes-apps\bootscript\bootscript.bb:
# # This file is the bootscript recipe. # SUMMARY = "Simple bootscript application" SECTION = "PETALINUX/apps" LICENSE = "MIT" LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302" SRC_URI = "file://bootscript \ " S = "${WORKDIR}" inherit update-rc.d INITSCRIPT_NAME = "bootscript" INITSCRIPT_PARAMS = "start 99 S ." do_install() { install -d ${D}${sysconfdir}/init.d install -m 0755 ${S}/bootscript ${D}${sysconfdir}/init.d/bootscript } FILES_${PN} += "${sysconfdir}/*"
Then update the project-spec\meta-user\recipes-apps\bootscript\files\bootscript with the i2cset commands (also attached below):
script
The script here is only provided for reference purpose only, and has not been fully tested!
This script should be auto-ran upon boot:
Debugging Linux Application on ZCU111:
Set up the LAN between your board and you PC:
Make sure you can ping your PC from the Linux console.
firewall
You may need to disable your firewall on your PC
Set up the Target Connection:
Drop down the Linux TCF Agent → Linux Agent
Select Add new target connection icon:
Next, right click on your application in Project explorer, and select Debug As → Debug Configurations:
Double Click on Xilinx C/C++ application (System Debugger)
Select Debug to launch the debug perspective.
Creating Linux Application in Petalinux:
Once the user is satisfied that everything works correct, then they can create a Linux Application in Petalinux:
petalinux-create --type apps --name rfdc-test --enable
Open the makefile at project-spec/meta-user/recipes-apps/rfdc-test/files and make the changes below:
APP = rfdc-test # Add any other object files to this list below APP_OBJS = xrfdc_selftest_example.o APP_OBJS += xrfdc_clk.o LDLIBS += -lmetal LDLIBS += -lrfdc all: $(APP) $(APP): $(APP_OBJS) $(CC) $(LDFLAGS) -o $@ $(APP_OBJS) $(LDLIBS) clean: -rm -f $(APP) *.elf *.gdb *.o $(APP_OBJS) %.o: %.c $(CC) -c -Wall -O0 -g3 -c -fmessage-length=0 -MT"$@" -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<"
Add the source files to the project-spec/meta-user/recipes-apps/rfdc_test/files directory.
Update the project-spec/meta-user/recipes-apps/rfdc-test/rfdc-test.bbappend file:
# # This file is the rfdc-test recipe. # SUMMARY = "Simple rfdc-test application" SECTION = "PETALINUX/apps" LICENSE = "MIT" LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302" SRC_URI = "file://xrfdc_selftest_example.c \ file://xrfdc_clk.c \ file://xrfdc_clk.h \ file://Makefile \ " S = "${WORKDIR}" DEPENDS = “libmetal" PROVIDES = "rfdc-test" do_compile() { oe_runmake } do_install() { install -d ${D}${bindir} install -m 0755 rfdc-read-write ${D}${bindir} }