Showing posts with label Buildroot. Show all posts
Showing posts with label Buildroot. Show all posts

Nov 15, 2015

Multi-monitor Buildroot x64 target

I am working on an application SW that displays to multiple monitors (somewhere between 2 to 4).  Eventually, I want to drive the multi-monitor display from an SoC, but working out the SW architecture with a multi-monitor GPU plugged into a PCIe slot of a modern PC is an excellent way to understand and derisk problems.  Before diving into a multi-monitor GPU, I can experiment with software supported multi-monitor setup in virtualbox.

NFS booting the virtual multi-monitor x64 target from a virtual Ubuntu server

In a previous blog entry, I PXE-booted a Buildroot x64 target (Dell Optiplex 755) from an Ubuntu server.  If I run both the target and the server as Virtualbox guests, I can use the Virtualbox internal network, which is completely a software network stack.  This is convenient when studying the kernel and software on a laptop, while away from the desktop server.

Setup virtualbox target

Created a virtualbox x64 guest with these settings:
  • General, Basic
    • Name: Target
    • Type: Linux
    • Version: Other Linux (64 bit)
  • System
    • Base memory: 512 MB
    • Boot order: Network ONLY
  • Display
    • Video memory: 64 MB
    • Monitor count: 4
    • Enable 3D acceleration
  • No storage, no audio, no serial port, no USB, no shared folder
  • Network:
    • 1st Adapter: NAT
    • 2nd adapter: internal network "intnet", which should MATCH the name of the 2nd adapter's internal network for the server.

Cross-compiling the target on the virtual Ubuntu server

WARNING: Buildroot must be extracted and then built on a filesystem that supports softlinks (so don't do this on an NTFS!).

After getting the latest stable Buildroot as shown in a previous blog entry, I configure Buildroot with the following options.
  • Target options: x86_64, corei2 (core7 selects SSE4 features, which Virtualbox does NOT support yet)
  • Build options:
    • enable ccache, but change the cache location to within the Buildroot directory (to avoid saving to the virtual HDD, and keep all work in the Vbox shared folder): $(TOPDIR)/.buildroot-ccache
    • Optimization level 3
  • Toolchain
    • glibc
    • Enable C++ support, necessary for Qt5
    • Build cross gdb for the host: does NOT build on the vbox server for some reason.  Besides, the native gdb should just work
    • Register toolchain within Eclipse Buildroot plug-in
  • System configuration
    • /dev management: eudev
  • Kernel
    • Using a custom--as supposed to in-tree--(def)config file: I need to add a couple of options for the NFS rootfs. I changed x86_64_defconfig from the kernel.org to create /home/henry/x64/BR2/kernel.config
  • Target packages
    • Debugging
      • gdb: only the gdbserver
    • Graphics
      • mesa3d
        • Gallium swrast (software OpenGL) driver
        • OpenGL EGL
        • OpenGL ES
      • Qt5
        • Approve free license
        • Compile and install examples
        • gui module
          • widgets
          • OpenGL: OpenGL ES 2.0 and opengl module
          • linuxfb support
          • eglfs support
          • Default platform: linuxfb
          • GIF, JPEG, PNG support
    • Hardware handling
      • lshw (does NOT even build!)
      • pciutils (lspci)
  • Networking applications
    • openssh: necessary to connect to the target from gdb on the server

Kernel config for NFS rootfs

The x86_64_defconfig alsready has a few options I needed, so I just added the following:

CONFIG_NETWORK_FILESYSTEMS=y
CONFIG_NFS_USE_KERNEL_DNS=y
CONFIG_E1000E=y

The stock x86_64_defconfig has CONFIG_E1000 but does NOT contain CONFIG_E1000E, so this is just a safety measure.

Build and deploy the binaries

After Buildroot make runs at the top level, its output/images will contain bzImage.  Copy this to the tftp download on the Virtualbox host.  On Windows, this is in C:\Users\<your uid>\.VirtualBox\TFTP folder, as shown here:

The rootfs images need to be extracted to the NFS export on the server:

~/o755/buildroot$ sudo tar -C /export/root/o755/ -xf ~/o755/buildroot/output/images/rootfs.tar

Setup the Virtualbox NFS server

Create a virtualbox x64 guest, with at least 2 CPUs, as much memory as possible, and the network with 2 adapters:
  • 1st adapter: NAT: for general Internet access (I am writing this blog on the host)
  • 2nd adapter: internal network: to communicate with the target.  Assign a static IP address 192.168.2.1 in Ubuntu Unity network settings, so that the target can refer to the NFS server with a static IP address (see below).
Virtualbox NAT network already has a PXE enabled DHCP server.  On Windows, the folder C:\Users\<your account>\.VirtualBox\TFTP plays the role of /var/lib/tftpboot (by default) for the Ubuntu hpa-tftp server.  So the following files should be put into that folder:
  • bzImage: compressed Linux kernel built by Buildroot, in its output/images (see above)
  • Target.pxe: I copied this file from Ubuntu desktop's /usr/lib/syslinux/pxelinux.0.  VirtualBox DHCP server has a rule for mapping the PXE image for each virtual box by its <name>.pxe.  Since my target's name is "Target", the PXE binary should be named Target.pxe.
  • menu.c32: This is the PXE menu program, copied verbatim from Ubuntu desktop's /usr/lib/syslinux/ folder.
  • pxelinux.cfg/ folder, which will contain the PXE menu entry
    • default: the catch-all menu.  PXE has lots of rules for matching the menu by the target's IP address, netmask, etc.  But default is the final fallback.  This file should point to the NFS rootfs the virtual server will host (see below).  My "default" file therefore looks like this:
DEFAULT menu.c32
PROMPT 0
MENU TITLE PXE Boot Menu
TIMEOUT 50 #This means 5 seconds
LABEL buildroot
 MENU LABEL buildroot kernel
 kernel o755Image
 append ip=192.168.2.3:192.168.2.1:192.168.2.1:255.255.255.0:o755:eth1:off root=/dev/nfs nfsroot=192.168.2.1:/export/root/o755 rw earlyprintk

The "ip" (used to be called nfsaddr) syntax is ip=<client-ip>:<server-ip>:<gw-ip>:<netmask>:<hostname>:<device>:<autoconf>

Setup the NFS server

The NFS server should get a static IP address.  I prefer to use the Ubuntu Unity tool, as you can see below.

Install the NFS server:

$ sudo apt-get install nfs-kernel-server

I configured the NFS server in /etc/exports, to serve any target in the "intnet":

/export         192.168.2.0/24(rw,fsid=0,insecure,no_subtree_check,async)

/export/root    192.168.2.0/24(rw,no_root_squash,no_subtree_check)


Remember to restart the NFS server after saving this file.  The root file system Buildroot generated should be expanded to the /export/root folder just mentioned, like this:

$ sudo mkdir /export/root/o755

$ sudo tar -C /export/root/o755/ -xf ~/o755/buildroot/output/images/rootfs.tar




nVidia Quadro multi-monitor video card

I bought an nVidia Quadro NVS 420 (Dell PN K722J)--I wanted an nVidia card because IMHO nVidia has the best Linux driver support--from eBay.  As you can see below, there was even a driver update earlier this year.
Now to the fine prints, from the driver README file: X is required, and glibc >= 2.0 is required.  The X server requirement is a deal-breaker for me: I want to keep the embedded distribution small.  Maybe a better alternative is to pick up an open source driver from the noveau project.  Quadro NVS 420 is supported under the NV50 Tesla family, code name NV98 (G98), as you can see on this page.

Buildroot config for Qt5, OpenCV, and nVidia Quadro 420 GPU

Since I am an embedded SW engineer, I treat even the PCs like targets (rather than desktops).  In a previous blog, I demonstrated an NFS booted Buildroot distribution for this Intel Core2 Duo Dell PC.  Except for updating Buildroot to the latest stable release (2015.02), I'll pick up from where I left off.

$ cd buildroot
$ git checkout 2015.02
$ git pull . 2015.02

The only difference in the Buildroot config between the virtual target and this real target is the Gallium nouveau driver (which supports all nVidia cards), under Target packages --> Graphics libraries and applications --> mesa3d.

To pick up the nouveau device driver, I added CONFIG_DRM_NOVEAU=y to the kernel defconfig file.

Also, I can connect over serial to the real target, by adding console=ttyS0,115200 to the kernel parameters in the pxelinux.cfg/default's "append" line.  During boot, the GPU is probed:

...
[    1.198462] nouveau  [  DEVICE][0000:03:00.0] BOOT0  : 0x298c00a2
[    1.204534] nouveau  [  DEVICE][0000:03:00.0] Chipset: G98 (NV98)
[    1.210605] nouveau  [  DEVICE][0000:03:00.0] Family : NV50
[    1.216169] nouveau  [   VBIOS][0000:03:00.0] checking PRAMIN for image...
[    1.233709] Console: switching to colour frame buffer device 160x64
[    1.245164] i915 0000:00:02.0: fb0: inteldrmfb frame buffer device
[    1.251332] i915 0000:00:02.0: registered panic notifier
[    1.307274] nouveau  [   VBIOS][0000:03:00.0] ... appears to be valid
[    1.313692] nouveau  [   VBIOS][0000:03:00.0] using image from PRAMIN
[    1.320231] nouveau  [   VBIOS][0000:03:00.0] BIT signature found
[    1.326303] nouveau  [   VBIOS][0000:03:00.0] version 62.98.6f.00.07
[    1.352876] nouveau 0000:03:00.0: irq 27 for MSI/MSI-X
[    1.352886] nouveau  [     PMC][0000:03:00.0] MSI interrupts enabled
[    1.359245] nouveau  [     PFB][0000:03:00.0] RAM type: GDDR3
[    1.364969] nouveau  [     PFB][0000:03:00.0] RAM size: 256 MiB
[    1.370868] nouveau  [     PFB][0000:03:00.0]    ZCOMP: 960 tags
[    1.378477] nouveau  [    VOLT][0000:03:00.0] GPU voltage: 1110000uv
[    1.915015] tsc: Refined TSC clocksource calibration: 2992.481 MHz
[    2.024020] nouveau  [  PTHERM][0000:03:00.0] FAN control: PWM
[    2.029844] nouveau  [  PTHERM][0000:03:00.0] fan management: automatic
[    2.036484] nouveau  [  PTHERM][0000:03:00.0] internal sensor: yes
[    2.062678] nouveau  [     CLK][0000:03:00.0] 03: core 169 MHz shader 358 MHz memory 100 MHz
[    2.071087] nouveau  [     CLK][0000:03:00.0] 0f: core 550 MHz shader 1400 MHz memory 700 MHz
[    2.079645] nouveau  [     CLK][0000:03:00.0] --: core 550 MHz shader 1400 MHz memory 702 MHz
[    2.088296] [TTM] Zone  kernel: Available graphics memory: 1001774 kiB
[    2.094802] [TTM] Initializing pool allocator
[    2.099147] [TTM] Initializing DMA pool allocator
[    2.103841] nouveau  [     DRM] VRAM: 256 MiB
[    2.108181] nouveau  [     DRM] GART: 1048576 MiB
[    2.112869] nouveau  [     DRM] TMDS table version 2.0
[    2.117987] nouveau  [     DRM] DCB version 4.0
[    2.122500] nouveau  [     DRM] DCB outp 00: 02000386 0f220010
[    2.128312] nouveau  [     DRM] DCB outp 01: 02000302 00020010
[    2.134124] nouveau  [     DRM] DCB outp 02: 040113a6 0f220010
[    2.139935] nouveau  [     DRM] DCB outp 03: 04011312 00020010
[    2.145747] nouveau  [     DRM] DCB conn 00: 00005046
[    2.150791] nouveau  [     DRM] DCB conn 01: 00006146
[    2.182206] [drm] Supports vblank timestamp caching Rev 2 (21.10.2013).
[    2.188798] [drm] Driver supports precise vblank timestamp query.
[    2.247926] nouveau  [     DRM] MM: using M2MF for buffer copies
[    2.287545] nouveau 0000:03:00.0: No connectors reported connected with modes
[    2.294654] [drm] Cannot find any crtc or sizes - going 1024x768
[    2.302510] nouveau  [     DRM] allocated 1024x768 fb: 0x60000, bo ffff88007a126c00
[    2.310181] fbcon: nouveaufb (fb1) is primary device
[    2.310182] fbcon: Remapping primary device, fb1, to tty 1-63
[    2.453168] nouveau 0000:03:00.0: fb1: nouveaufb frame buffer device
[    2.459500] [drm] Initialized nouveau 1.2.1 20120801 for 0000:03:00.0 on minor 1

The GPU shows up as another framebuffer device /dev/fb1 (/sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0/0000:02:00.0/0000:03:00.0/graphics/fb1).  The multiple folder level corresponds to the PCI switches built into the card (apparently), as can be seen from the lspci output:

01:00.0 PCI bridge: NVIDIA Corporation NF200 PCIe 2.0 switch for Quadro Plex S4 / Tesla S870 / Tesla S1070 / Tesla S2050 (rev a3)
02:00.0 PCI bridge: NVIDIA Corporation NF200 PCIe 2.0 switch for Quadro Plex S4 / Tesla S870 / Tesla S1070 / Tesla S2050 (rev a3)
02:02.0 PCI bridge: NVIDIA Corporation NF200 PCIe 2.0 switch for Quadro Plex S4 / Tesla S870 / Tesla S1070 / Tesla S2050 (rev a3)
03:00.0 VGA compatible controller: NVIDIA Corporation G98 [Quadro NVS 420] (rev a1)

I can see Qt GUI drawing to fb1 by running Qt examples like these:

/usr/lib/qt/examples/opengl/2dpainting/2dpainting -platform linuxfb:fb=/dev/fb1

Of course, OpenGL display doesn't work against the linuxfb platform.  But there seems to be a problem with the mesa3d nouveau platform I compiled, because gbm cycles through a few Gallium drivers EXCEPT the one I actually built: nouveau_dri, which is even in the search path (/usr/lib/dri).

# ./application -platform eglfs
gbm: failed to open any driver (search paths /usr/lib/dri)
gbm: Last dlopen error: /usr/lib/dri/i915_dri.so: cannot open shared object file: No such file or directory
...
Could not initialize egl display

I realized that eglfs platform is querying the framebuffer property from /dev/fb0 even though I set the environment variable QT_QPA_EGLFS_FB to /dev/fb1.  So I removed the Intel GPU from the picture by commenting out the Intel GPU drivers from the kernel config.  And now I see this message:

# ./openglwindow -platform eglfs
Unable to query physical screen size, defaulting to 100 dpi.
To override, set QT_QPA_EGLFS_PHYSICAL_WIDTH and QT_QPA_EGLFS_PHYSICAL_HEIGHT (in millimeters).
EGL Error : Could not create the egl surface: error = 0x300b
Aborted

The error (EGL_BAD_NATIVE_WINDOW) is logged in src/plugins/platforms/eglfs/qeglfswindow.cpp, QEglFSWindow::resetSurface(), but thrown in eglCreateWindowSurface:

    if (!rx::IsValidEGLNativeWindowType(win))
    {
        recordError(egl::Error(EGL_BAD_NATIVE_WINDOW));
        return EGL_NO_SURFACE;
    }

This begs the question: what is a Qt native window?  I decided that I still do NOT know the low level graphics SW stack, and just stick to either linuxfb or directfb.

directfb: the lowest level display abstraction in userspace

DirectFB (Direct Frame Buffer) is a software library with a small memory footprint that provides graphics acceleration, input device handling and abstraction layer, and integrated windowing system with support for translucent windows and multiple display layers on top of the Linux framebuffer without requiring any kernel modifications.  DirectFB allows applications to talk directly to video hardware through a direct API, speeding up and simplifying graphic operations.

But directfb does not support Quadro (noveau) drivers?  See directfb/gfxdrivers/nvidia/nvidia.c  Platform independent examples src is in buildroot/output/build/directfb-examples-1.6.0/srcles

Each GPU detected by DRM is referred as a DRM device, and a device file /dev/dri/cardX (where X is a sequential number) is created to interface with it, as in this example for the NVS420 card in a PC:

# ls /dev/dri
card0       controlD64  renderD128

Note that on Zedboard which lacks a GPU, Lars Clausen (of ADI)'s adv7511 DRM driver does not offer the renderD128 file:

# ls /dev/dri
card0       controlD64

User space programs that want to talk to the GPU must open the file and use ioctl calls to communicate with DRM. Different ioctls correspond to different functions of the DRM API.  A library called libdrm was created to facilitate the interface of user space programs with the DRM subsystem, as shown here:

This library is merely a wrapper that provides a function written in C for every ioctl of the DRM API, as well as constants, structures and other helper elements.  DRM consists of two parts: a generic "DRM core" and a specific one ("DRM Driver") for each type of supported hardware.  DRM driver, on the other hand, implements the hardware-dependent part of the API, specific to the type of GPU it supports; it should provide the implementation to the remainder ioctls not covered by DRM core, but it may also extend the API offering additional ioctls with extra functionality, for which extra userspace library is offered.  For the nVidia card, we therefore see:

# ls /usr/lib/libdrm*
/usr/lib/libdrm.so.2.4.0   /usr/lib/libdrm_nouveau.so.2.0.0

But strangely, Zedboard has enumerated a non-existent card (Adreno)?

# ls -Lh /usr/lib/libdrm*
/usr/lib/libdrm.so.2.4.0   /usr/lib/libdrm_freedreno.so.1.0.0

GEM (graphics executaion manager) manages graphics buffers.  Through GEM, a user space program can create, handle and destroy memory objects living in the GPU's video memory.  Confusingly, there are mesa3d userspace drivers that use the kernel drivers, as you can see in the AMD example below:

As the demand for better graphics increased, hardware manufacturers created a way to decrease the amount of CPU time required to fill the framebuffer. This is commonly called "graphics accelerating".  Common graphics drawing commands (many of them geometric) are sent to the graphics accelerator in their raw form. The accelerator then rasterizes the results of the command to the framebuffer.

Debugging a full screen directfb application

The df_fire example did NOT run against the nouveau driver, so let's debug into it.  Following relevant build variables are in the Makefile:

CFLAGS = -Wall -O3 -pipe -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64  -pipe -O3  -Werror-implicit-function-declaration
CPPFLAGS = -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
DIRECTFB_CFLAGS = -D_REENTRANT -I/home/henry/work/o755/buildroot/output/host/usr/x86_64-buildroot-linux-gnu/sysroot/usr/include/directfb  
DIRECTFB_LIBS = -ldirectfb -lfusion -L/home/henry/work/o755/buildroot/output/host/usr/x86_64-buildroot-linux-gnu/sysroot/usr/lib -ldirect -lpthread  
AM_CFLAGS = -D_REENTRANT -I/home/henry/work/o755/buildroot/output/host/usr/x86_64-buildroot-linux-gnu/sysroot/usr/include/directfb   -D_GNU_SOURCE
LIBADDS = \
        -ldirectfb -lfusion -L/home/henry/work/o755/buildroot/output/host/usr/x86_64-buildroot-linux-gnu/sysroot/usr/lib -ldirect -lpthread  

AM_CPPFLAGS = \
        -DDATADIR=\"${datarootdir}/directfb-examples\" \
        -DFONT=\"$(fontsdatadir)/decker.ttf\"

Trying the Quadro NVS 420 nVidia card (model G98) on Ubuntu desktop

I could not get Ubuntu to see the DLP 3010 evaluation module, so I checked whether the nVidia proprietary drivers could The list of driver updates can be queried:

henry@o755:~$ sudo ubuntu-drivers devices
== /sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0/0000:02:00.0/0000:03:00.0 ==
model    : G98 [Quadro NVS 420]
modalias : pci:v000010DEd000006F8sv000010DEsd0000057Ebc03sc00i00
vendor   : NVIDIA Corporation
driver   : nvidia-304-updates - distro non-free
driver   : xserver-xorg-video-nouveau - distro free builtin
driver   : nvidia-331 - distro non-free recommended
driver   : nvidia-331-updates - distro non-free
driver   : nvidia-304 - distro non-free
driver   : nvidia-173 - distro non-free

An easy way to install the proprietary device drivers is through System Settings (unity-control-center) --> System --> Software & Updates --> Additional Drivers.  Even after installing the nVidia proprietary driver, the 2nd monitor would not enumerate; I had to run the nVidia Xserver settings tool, and explicitly detect the display for the Xserver settings to update.  I think this means that nouveau driver cannot drive NVS420 card in multi-monitor mode.
Indeed, the computer only sees 1 framebuffer device:

$ ls -lhg /sys/class/graphics/
total 0
lrwxrwxrwx 1 root 0 Apr 25 12:06 fb0 -> ../../devices/pci0000:00/0000:00:02.0/graphics/fb0
lrwxrwxrwx 1 root 0 Apr 25 11:50 fbcon -> ../../devices/virtual/graphics/fbcon

As this nouveau multi-monitor setup explanation shows, it is the X server that lays out the multi-monitor on the desktop.  Multiple monitors has to be organized as 1 logical screen, because an X application can only display to 1 screen (i.e. application cannot choose to run on multiple screens, nor change from 1 screen to another dynamically).

Oct 7, 2014

Qt5 GUI on Zedboard

Most Linux distributions (Ubuntu being the most popular right now) use the X windowing system for the display.  Since I don't need a full-blown graphical desktop on an embedded hardware controller but do need a sexy GUI application that runs in full screen mode, QT embedded is a good fit for my needs.  One can certainly cross-compile QT using any toolchain (as shown in this example), but since I have been creating my root file system in Buildroot, I want to modify the Buildroot config to pickup Qt as well.

Optional: minimal Zedboard Linux kernel config that works with Qt Embedded

Linux kernel and modules are complicated, and there are way too many modules even to count.  Being a minimalist, I wanted to whittle down even the kernel to the smallest size that supports running a Qt GUI.  The true minimal set is impossible to obtain: there are just too many dependencies, and my needs many change soon.  This is what I removed from the current working Zedboard kernel that boots from TFS, supports NFS rootfs:
  • Remove all SPI related modules, even the QSPI, which is the 1st stage boot device I want to use.  It's OK because Linux is NOT responsible for booting itself.
  • Remove most of the USB HID device drivers, except for Logtech and Microsoft
  • Remove everything I could see under IIO
    • CONFIG_IIO
  • Remove all LED related configs.
  • Remove HRTIMER
  • Removed KGDB--does NOT seem to work for debugging startup problems, which is where most the problems are
    • But leave CONFIG_DEBUG_INFO and CONFIG_FRAME_POINTER: still helps with JTAG debugging
  • Removed FTRACE related configs
Note that I2C still remains, in order to drive the ADV7511 HDMI display!

Buildroot config that includes Qt Embedded

From a minimal uClinux root file system as the baseline, I made the following changes:
  • Toolchain:
    • Buildroot toolchain
    • Kernel headers: pick something close to my kernel, which is the ADI kernel (version 3.15 when I downloaded it)
    • C library: glibc seems to be less finicky than uClibc, and it is also required for CUDA
    • Threading: Native POSIX (NPTL)
    • C++ support
    • tls (thread local storage) support
    • Build cross gdb for the host, AND select Python support.   Otherwise, you will see this message when trying to cross debug a Qt application:
    • Purge unwanted locales (except en_US)
    • Register toolchain within Eclipse Buildroot plug-in (to build my own programs in Eclipse)
  • Kernel
    • I have my own kernel (based on ADI's 3.15 kernel)
      • Local directory: /mnt/work/zed/kernel
      • Kernel configuration: defconfig (zynq_xcomm_adv7511_nfs)
      • Kernel binary format: uImage
      • Load address: 0x8000
    • Device tree support
      • Change the DTS file from zynq-zed to zynq-zed-adv7511.  While debugging why I am not seeing /dev/fb0, I found out that the ADV7511 device does not show up in /proc/device-tree (according to kernel/arch/arm/boo/dts/zyhnq-zed-adv7511.dts, I was looking for fpga-axi@0), and finally realized that the zedbord Buildroot config checked into buildroot does NOT display!
  • Target packages:
    • Debugging: just gdb (gdbesrver) for cross debugging.  May add oprofile and valgrind later.
    • Graphic libraries and applications:
      • directfb
        • Do NOT choose /dev/input/eventX input driver, because the latest CHIPIDEA USB driver enumerates the keyboard twice, leading to doubling of each keystroke (for example, typing "dir" yields "ddiirr").
        • compile keyboard input driver
        • compile PS2 mouse input driver (also supports USB mouse)
        • For me, no need for touchscreen support
      • FORGET about mesa3d-demos, which requires Xwindows
      • But DO consider the QT libraries in the future
        • qextserialport if the application needs to control serial devices
        • qwt: 2D plotting
      • FORGET about mesa3d, which allows OpenGL EGL and OpenGL ES packages.  Qt does not require OpenGL, and my unsexy Qt GUI can get by without 3D rendering
      • Do NOT select Qt--the Qt4 (Qt Embedded), because I want Qt5 (the newer release).  BUT if you do want it,
        • Note that only Qt standard is NOT available without an X-windows (which I do NOT want)
        • Compile and install Qt demos (with code)
        • Library type: static library, because I just want to copy 1 file to the target
        • Approve free license
        • Gui module
          • freetype2 support: Qt freetype2
          • JPEG support: system libjpeg
          • PNG support: system libpng
          • TIFF support: system libtiff
        • OpenGL ES v2.x support: does NOT build, at least for mesa on ARM
        • Uncheck Script Module
        • Graphics drivers: note that only linuxfb is selected (because I did not enable direcfb).  multiscreen driver looks interesting, but Zedboard can only driver 1 monitor.
        • Mouse drivers: linux input (to support USB mouse)
        • Keyboard drivers: linux input (to support USB mouse)
      • Qt5 (and only Qt5)
        • Approve free license
        • Compile and install examples (with code)
        • gui module
          • widgets module
          • Choose directfb platform, and uncheck OpenGL support, which is required for eglfs support
          • Default graphical platform: directfb
          • GIF, JPEG, PNG support
        • Do NOT check qt5imageformats (for TIFF support); it requires web support, which I do NOT want.
    • Libraries
      • Hardware handling: check libftdi (because I have experience integrating a distributed system over FTDI D2XX USB SDK), with C++ bindings (because I might use the Qt C++ API)
      • Graphics
        • tiff: in biotech, raw image files are saved in tiff
        • opencv, with tiff and turn off calib3d, legacy, ts, video, videostab modules.  Do NOT choose qtbackend support, which turns OFF Qt5
    • Networking applications
      • openssh: necessary for remote gdb debugging
      • ntp: if the host will be network enabled, it's nice to have accurate time
    • Shells and utilities:
      • Optional: logrotate: application WILL log, and it will have to be rotated.
      • file, screen: indispensable development tools
Note that I am still using EABI toolchain, rather than EABIhf (to try later).  I found that Qt takes a *long* time to build; the resulting rootfs.tar is 81 MB even after stripping out all other locales except for US-en.

1st try fails: adv7511 driver probe error -110 (ETIMEOUT)

When I change the DTS file from zynq_zed to zynq_zed_adv7511 in Buildroot config, I still don't have /dev/fb0, but at least the kernel initializes DRM, as you can see below:

xdevcfg f8007000.devcfg: ioremap 0xf8007000 to e0816000
[drm] Initialized drm 1.1.0 20060810
drivers/gpu/drm/adi_axi_hdmi/axi_hdmi_drv.c:axi_hdmi_platform_probe[176]
platform 70e00000.axi_hdmi: Driver axi-hdmi requests probe deferral
...
i2c /dev entries driver
...
adv7511: probe of 0-0039 failed with error -110

zynq-edac f8006000.ps7-ddrc: ecc not enabled
...
adv7511-hdmi-snd adv7511_hdmi_snd.3: ASoC: CODEC (null) not registered
platform adv7511_hdmi_snd.3: Driver adv7511-hdmi-snd requests probe deferral

zed-adau1761-snd zed_sound.4: adau-hifi <-> 77600000.axi-i2s mapping ok

It looks like the HDMI sound HW initialized OK, but the ADV7511 video driver has a problem, so I have to look inside the ADI supplied device driver in <kernel>/drivers/gpu/drm/i2c/adv7511_core.c.  It would appear that this is the right device driver for the <kernel>/arch/arm/boot/dts/zynq-zed-adv7511.dts, because the device properties specified in the DTS are parsed in the adv7511_parse_dt() function, as shown in the side by side comparison below:

zynq-zed-adv7511.dts adv7511@39 property static int adv7511_parse_dt ( struct device_node *np, struct adv7511_link_config *config)
compatible = "adi,adv7511";
reg = <0x39>;
adi,input-style = <0x02>;
adi,input-id = <0x01>;
adi,input-color-depth = <0x3>;
adi,sync-pulse = <0x03>;
adi,bit-justification = <0x01>;
adi,up-conversion = <0x00>;
adi,timing-generation-sequence = <0x00>;
adi,vsync-polarity = <0x02>;
adi,hsync-polarity = <0x02>;
adi,tdms-clock-inversion;
adi,clock-delay = <0x03>;
of_property_read_u32(np,
  "adi,input-id", &config->id);

config->sync_pulse =
  ADV7511_INPUT_SYNC_PULSE_NONE;

of_property_read_u32(np,
  "adi,sync-pulse",
   &config->sync_pulse);

...

config->gpio_pd =
  of_get_gpio(np, 0);
if (config->gpio_pd
    == -EPROBE_DEFER)
  return -EPROBE_DEFER;


return 0;

The DT parsing function is called from the ".probe" function adv7511_probe:

static const struct i2c_device_id adv7511_ids[] = {
        { "adv7511", 0 },
        {}
};

static struct drm_i2c_encoder_driver adv7511_driver = {
        .i2c_driver = {
                .driver = {
                        .name = "adv7511",
                },
                .id_table = adv7511_ids,
                .probe = adv7511_probe,
                .remove = adv7511_remove,
        },

        .encoder_init = adv7511_encoder_init,
};
static int __init adv7511_init(void)
{
  return drm_i2c_encoder_register(THIS_MODULE, &adv7511_driver);

}
module_init(adv7511_init);

According to the DTS, "adv7511" is a slave of the axi_hdmi@70e00000

zynq-zed-adv7511.dts axi_hdmi@70e00000 property<kernel>/drivers/gpu/drm/adi_hdmi_drv.c
axi_hdmi@70e00000 {
  compatible =
   "adi,axi-hdmi-tx-1.00.a";
  reg = <0x70e00000 0x10000>;
  encoder-slave = <&adv7511>;
  dmas = <&axi_vdma_0 0>;
  dma-names = "video";
  clocks = <&hdmi_clock>;
};
static struct platform_driver adv7511_encoder_driver = {
  .driver = {
    .name = "axi-hdmi",
    .owner = THIS_MODULE,
    .of_match_table = adv7511_encoder_of_match,
  },
  .probe = axi_hdmi_platform_probe,
  .remove = axi_hdmi_platform_remove,
};
module_platform_driver(adv7511_encoder_driver);

I know which function to read for why I saw " Driver axi-hdmi requests probe deferral" in dmesg earlier: on line 176 of the <kernel>/driveres/gpu/drm/adi_hdmi_drv.c, the clock is probably not ready yet:

174  private->hdmi_clock = devm_clk_get(&pdev->dev, NULL);
175  if (IS_ERR(private->hdmi_clock)) {
176    printk("%s:%s[%d]\n", __FILE__, __func__, __LINE__);
177    return -EPROBE_DEFER;
178  }
179
180  slave_node = of_parse_phandle(np, "encoder-slave", 0);
181  if (!slave_node)
182    return -EINVAL;
...
193  private->encoder_slave = of_find_i2c_device_by_node(slave_node);
194  of_node_put(slave_node);
195
196  if (!private->encoder_slave || !private->encoder_slave->dev.driver) {
197    printk("%s:%s[%d]\n", __FILE__, __func__, __LINE__);
198    return -EPROBE_DEFER;
199  }

Note that since the deferral happens even before parsing the "encode-slave" (the adv7511), the adv7511 driver will not even get initialized till later.  The clock being discussed is the <&hdmi_clock> in DTS But does it ever become ready?  I think so, because dmesg shows another axi-hdmi driver probe deferral a few moments later:
...
drivers/gpu/drm/adi_axi_hdmi/axi_hdmi_drv.c:axi_hdmi_platform_probe[197]
platform 70e00000.axi_hdmi: Driver axi-hdmi requests probe deferral
adv7511-hdmi-snd adv7511_hdmi_snd.3: ASoC: CODEC (null) not registered
platform adv7511_hdmi_snd.3: Driver adv7511-hdmi-snd requests probe deferral

If the axi-hdmi probe failed at checking the encoder slave (which is expected, since the slave probe died with error code -110--ETIMEOUT) a few lines below the clock checking, it must mean that the clock was OK the 2nd time around.  This time, the error is because the encoder_slave (the <&adv7511> in DTS) or the slave's device driver is NULL; since the slave has to be found through I2C bus search--HW operation--I would guess this is the problem.

Turn on I2C kernel driver debugging

To get a better visibility into the I2C discovery failure, I added kernel configs CONFIG_I2C_DEBUG_CORE=y and CONFIG_I2C_DEBUG_BUS=y in my defconfig zynq_zed_adv7511_nfs_defconfig.  Here is the modified dmesg, showing only new/different lines from the failure case (note that the I2C driver debugging is now on):

platform 70e00000.axi_hdmi: Driver axi-hdmi requests probe deferral
i2c-core: driver [adv7511] registered
i2c-core: driver [at24] registered
...
i2c-dev: adapter [xiic-i2c] registered as minor 0
i2c i2c-0: adapter [xiic-i2c] registered
i2c i2c-0: of_i2c: walking child nodes
i2c i2c-0: of_i2c: register /fpga-axi@0/i2c@41600000/adv7511@39
i2c 0-0039: uevent
adv7511 0-0039: probe
i2c i2c-0: master_xfer[0] W, addr=0x39, len=2
xiic-i2c 41600000.i2c: xiic_xfer entry SR: 0xc0
xiic-i2c 41600000.i2c: __xiic_start_xfer entry, msg: d7451c34,...
xiic-i2c 41600000.i2c: xiic_start_send entry, msg: d7451c34,len: 2
xiic-i2c 41600000.i2c: xiic_start_send entry, ISR: 0xd0, CR: 0x1... 
xiic-i2c 41600000.i2c: xiic_fill_tx_fifo entry, len: 2, fifo space: 15 
xiic-i2c 41600000.i2c: xiic_fill_tx_fifo TX STOP
adv7511: probe of 0-0039 failed with error -110
i2c i2c-0: client [adv7511] registered with bus id 0-0039
...
adau1761 0-003b: probe
i2c-core: driver [adau1761] registered
adv7511-hdmi-snd adv7511_hdmi_snd.3: ASoC: CODEC (null) not registered
platform adv7511_hdmi_snd.3: Driver adv7511-hdmi-snd requests platform adv7511_hdmi_snd.3: Driver adv7511-hdmi-snd requests
i2c i2c-0: master_xfer[0] W, addr=0x3b, len=2
i2c i2c-0: master_xfer[1] R, addr=0x3b, len=1
xiic-i2c 41600000.i2c: xiic_xfer entry SR: 0xc0
xiic-i2c 41600000.i2c: __xiic_start_xfer entry, msg: d7451d28,...
xiic-i2c 41600000.i2c: xiic_start_send entry, msg: d7451d28, len: 2
xiic-i2c 41600000.i2c: xiic_start_send entry, ISR: 0xd0, CR: 0x1
xiic-i2c 41600000.i2c: xiic_fill_tx_fifo entry, len: 2, ...
i2c i2c-0: master_xfer[0] W, addr=0x3b, len=3
xiic-i2c 41600000.i2c: xiic_xfer entry SR: 0x8c
...
zed-adau1761-snd zed_sound.4: adau-hifi <-> 77600000.axi-i2s mapping ok
i2c i2c-0: master_xfer[0] W, addr=0x3b, len=3
...
xiic-i2c 41600000.i2c: xiic_xfer entry SR: 0x8c
...
drivers/gpu/drm/adi_axi_hdmi/axi_hdmi_drv.c:axi_hdmi_platform_probe[197]

The difference between the success (adau1761) and the failure (adv7511) case is that the message to adau1761 (which also gets put into xiic_fill_tx_fifo?) is perhaps acknowledged, and more importantly the device driver can continue to the next message.  [But shouldn't even the successful case eventually come to exhaust the tx_fifo?]

The end result for the video and sound HDMI drivers are the same as before turning on I2C debug--I guess that's somewhat reassuring, but I am disappointed by no additional verbosity around where the I2C bus searches for ADV7511.  What the new dmesg log shows is that adv7511 probe begins, and the I2C transmission stops early, then the probe fails with -110 (ETIMEOUT).  The difference between TX STOP and sustained (at least for a few rounds) I2C transfer are the address (0x39 vs. 0x3B) and the message pointers (d7451c34 vs. d7451d28; but both messages are the same length--probably a handshake); in both cases, the master is the same (41600000.i2c: the parent of 39.adv7511 and 3b.adau1761 according to the DTS).

The "xiic_xfer entry SR" snippet above finally clues me into the function that is returning ETIMEOUT: <kernel>/drivers/i2c/busses/i2c-xiic.c, which is compatible with "xlnx,xps-iic-2.00.a" in the DTS.  The actual start of transfer is in xiic_start_xfer():

static void xiic_start_xfer(struct xiic_i2c *i2c)
{
  unsigned long flags;

  spin_lock_irqsave(&i2c->lock, flags);
  xiic_reinit(i2c);
  /* disable interrupts globally */
  xiic_setreg32(i2c, XIIC_DGIER_OFFSET, 0);
  spin_unlock_irqrestore(&i2c->lock, flags);

  __xiic_start_xfer(i2c);
  xiic_setreg32(i2c, XIIC_DGIER_OFFSET, XIIC_GINTR_ENABLE_MASK);
}

The actual start of message is defined in xiic_start_send, called from __xiic_start_xfer() above:

if (!(msg->flags & I2C_M_NOSTART)) {
  /* write the address */
  u16 data = ((msg->addr << 1) & 0xfe) | XIIC_WRITE_OPERATION |
                        XIIC_TX_DYN_START_MASK;
  if ((i2c->nmsgs == 1) && msg->len == 0)
    /* no data and last message -> add STOP */
    data |= XIIC_TX_DYN_STOP_MASK;

  xiic_setreg16(i2c, XIIC_DTR_REG_OFFSET, data);
}

Since the message size is 2, XIIC_TX_DYN_STOP_MASK will not be or'ed, so that the 16 bit message written to the data TX register should just be (0x39 << 1) | 0 | 0x0100 = 0x0172.  There is of course a lot of code between the above call of 16 bit register write on PS0 to when Zynq's I2C on-chip peripheral HW starts shaking the SDA/SCL.  But since the ADV7511 chip is sitting a couple of inches away from Zynq on the Zedboard, the ADV7511's SDA/SCL pins (pins 56 and 55) can only be shaken by the IO Zynq pins.  Back tracing ADV7511's SDA/SCL on the schematic, I find the other end of the 2 traces on BANK33 (VCC3V3), Y16/AA18, which appear in zed_system_constr.xdc in Vivado project:

set_property  -dict {PACKAGE_PIN  R7    IOSTANDARD LVCMOS33} [get_ports iic_scl]
set_property  -dict {PACKAGE_PIN  U7    IOSTANDARD LVCMOS33} [get_ports iic_sda]
set_property  -dict {PACKAGE_PIN  AA18  IOSTANDARD LVCMOS33 PULLTYPE PULLUP} [get_ports iic_mux_scl[1]]
set_property  -dict {PACKAGE_PIN  Y16   IOSTANDARD LVCMOS33 PULLTYPE PULLUP} [get_ports iic_mux_sda[1]]
set_property  -dict {PACKAGE_PIN  AB4   IOSTANDARD LVCMOS33 PULLTYPE PULLUP} [get_ports iic_mux_scl[0]]
set_property  -dict {PACKAGE_PIN  AB5   IOSTANDARD LVCMOS33 PULLTYPE PULLUP} [get_ports iic_mux_sda[0]]
So according to the constraint, the 1st output of the I2C mux is going to the ADAU1761 chip, and the 2nd output is going to the ADV7511 chip.  Behind IOBUFs that switch between output state and high impedance input state, sys_i2c_mixer should be handling the iic_scl inputs and outputs, as shown below in a portion of this Zynq design.
The mixer is a relative simple (2 VHDL files) logic that takes orders from AXI_IIC logic which emits the T (tristate) O (output) signals and takes the I (input) SCL/SDA signals.

Also in the top level module, iic_mux_scl The IO pins R7/U7 constrained to iic_scl/sda above are going to the FMC connector, and wired to iic_fmc_{csl|sda}_io in the top level module (system_top.v), which get connected to IIC_FMC_scl (part of IIC_FMC port) when I hop over an IOBU <--> axi_iic_fmc logic <--> axi_cpu_interconnect[M06_AXI] <--> M_AXI_GP0 in the design.

To debug the I2C messaging, I will begin by capturing the iic_mux_{scl|sda}_{i|o}[0,1] and the iic_mux_{scl|sda}_t wires: 10 wires total.  A chance to use my Hantek USB 16 bit logic analyzer!  But first, let's specify the IO constraints to the 2 Pmod connectors on the Zedboard (total of 4).  On the Zedboard schematic, JA1~4/7~10 are on the JA1 Pmod connector:
Similarly, JB1~4/7~10 are on the JB1 Pmod connector.  The Zynq IO pins to constrain can also be looked up in the schematic (3.3V bank 13) and added to the constraint file:

  • JA1 = Y11, JA2 = AA11, JA3 = Y10, JA4 = AA9, JA7 = AB11, JA8 = AB10, JA9 = AB9, JA10 = AA8
  • JB1 = W12, JB2 = W11, JB3 = V10, JB4 = W8, JB7 = V12, JB8 = W10, JB9 = V9, JB10 = V8
set_property -dict {PACKAGE_PIN Y11  IOSTANDARD LVCMOS33} [get_ports DebugA[0]]
set_property -dict {PACKAGE_PIN AA11 IOSTANDARD LVCMOS33} [get_ports DebugA[1]]
set_property -dict {PACKAGE_PIN Y10  IOSTANDARD LVCMOS33} [get_ports DebugA[2]]
set_property -dict {PACKAGE_PIN AA9  IOSTANDARD LVCMOS33} [get_ports DebugA[3]]
set_property -dict {PACKAGE_PIN AB11 IOSTANDARD LVCMOS33} [get_ports DebugA[4]]
set_property -dict {PACKAGE_PIN AB10 IOSTANDARD LVCMOS33} [get_ports DebugA[5]]
set_property -dict {PACKAGE_PIN AB9  IOSTANDARD LVCMOS33} [get_ports DebugA[6]]
set_property -dict {PACKAGE_PIN AA8  IOSTANDARD LVCMOS33} [get_ports DebugA[7]]

set_property -dict {PACKAGE_PIN W12  IOSTANDARD LVCMOS33} [get_ports DebugB[0]]
set_property -dict {PACKAGE_PIN W11  IOSTANDARD LVCMOS33} [get_ports DebugB[1]]
set_property -dict {PACKAGE_PIN V10  IOSTANDARD LVCMOS33} [get_ports DebugB[2]]
set_property -dict {PACKAGE_PIN W8   IOSTANDARD LVCMOS33} [get_ports DebugB[3]]
set_property -dict {PACKAGE_PIN V12  IOSTANDARD LVCMOS33} [get_ports DebugB[4]]
set_property -dict {PACKAGE_PIN W10  IOSTANDARD LVCMOS33} [get_ports DebugB[5]]
set_property -dict {PACKAGE_PIN V9   IOSTANDARD LVCMOS33} [get_ports DebugB[6]]
set_property -dict {PACKAGE_PIN V8   IOSTANDARD LVCMOS33} [get_ports DebugB[7]]

Then connect the DebugA/B wires at the top module.


  output[7:0] DebugA, DebugB;
  //Send to logic analyzer through Pmod JA and JB
  wire PLCLK0, PLnRST;
  assign DebugA[0] = PLnRST;            //channel0
  assign DebugA[1] = PLCLK0;            //channel1
  assign DebugA[2] = iic_mux_scl_t_s;   //channel2
  assign DebugA[3] = iic_mux_sda_t_s;   //channel3
  assign DebugA[4] = iic_mux_scl_o_s[0];//channel4
  assign DebugA[5] = iic_mux_sda_o_s[0];//channel5
  assign DebugA[6] = iic_mux_scl_i_s[0];//channel6
  assign DebugA[7] = iic_mux_sda_i_s[0];//channel7

  assign DebugB[0] = iic_mux_scl_o_s[1];//channel8
  assign DebugB[1] = iic_mux_sda_o_s[1];//channel9
  assign DebugB[2] = iic_mux_scl_i_s[1];//channel10
  assign DebugB[3] = iic_mux_sda_i_s[1];//channel11

The Pmod connectors are connected to the logic analyzer as shown in the picture below:

PLnRST and PLCLK0 are the 2 ports I created in the Zynq graphical configuration, as a sanity check when looking at the logic analyzer output, which looks like this:
FCLK_CLK0 is shaking at 500 us period when it should be a 100 MHz clock because of aliasing (this $99 16 channel logic analyzer can only sample up to 48 MHz--compare that to the $500 Saleae logic analyzer that samples at 500 MHz: you really do get what you pay for for the measurement tools).  When I expose the sdif_mclk at 12.8 MHz, the logic analyzer can barely keep up with it.  On the other hand, the Hantek DSO2250 250 MHz scope can show the 100 MHz PLCLK without a problem, showing that using the Pmod connector for logic analyzer debugging is a viable technique.  The biggest problem with the Hantek analyzer is inability to trigger.

Nevertheless, the INPUT SCL/SDA are bouncing, as you can see below (iic_mux_scl_i_s[1] in green and iic_mux_sda_i_s[1] in yellow):
and very shortly thereafter:

iic_mux_{sda|scl}_i_s are the OUTPUTs of the IOBUF (when Tristate is FALSE) and feeds the sys_i2c_mixer in the Zynq design, whereas iic_mux_{sda|scl}_o_s, which are the OUTPUTs of the sys_i2c_mixer show no activity.  But why would the I2C slave (ADV7511) respond with packets unless it received something from the master (sys_i2c_mixer)?

While thinking about whether/how to dig deeper into I2C, it occurred to me that SOME I2C communication is taking place, and it is unlikely that the messaging is broken in a bizarre way.  In my experience, things are usually broken in an OBVIOUS way (in hindsight).  So I started thinking about some OBVIOUS ways that I2C messaging to the ADV7511 could be broken, and the 1st thing that came to my mind were the master/slave modes and the address confusion.  According to the DTS (shown way above), the slave address is 0x39, so I was confused when reading ADV7511_Programming_Guide.pdf "Section 4 - Programming Tasks", which does NOT list 0x39 among the I2C addresses.  Instead, it says that if the PD/AD pin (pin 38 of the IC6 on the Zedboard) is tied to ground, then the I2C address is 0x72 (else 0x7A).  But on the same schematic, it says "I2C Address 0111001 R/W", so 0x39 must the right address.  And indeed, when I try changing the address to 0x72 or 0x7A, it still fails the same way.  So much for that guess.

When I exposed the wires from/to axi_iic_main to the scope and looked at the SCL in and out, I was surprised that there is absolutely no activity on the output from the axi_iic_main, which does NOT agree with the fact that the video and audio ICs should be I2C slaves.  Another surprise is that the signals are absolutely identical between the 1st and the 2nd inputs into the iic_mixer, as you can see below.
Even in the case of the working bare metal (no Linux) HDMI example, the output wires are complete, as you can see below:

Another bizarre thing is that the tristate (yellow in the capture below) is just the same as the scl, whereas I expected it to stay low the whole time the I2C master is transmitting.

When I read the raw HDMI code, I noticed that the iic_main is bypassed altogether!  For example, in SetVideoResolution(), the clkgen and HDMI video IP are controlled; but axi_iic_main is not involved.

Xil_Out32(CF_CLKGEN_BASEADDR + AXI_CLKGEN_V2_REG_DRP_CNTRL, 0x00);

Similarly, video is controlled through registers offset from either CFV_BASEADDR (axi_hdmi_core base address) or VDMA_BASE_ADDR (axi_hdmi_dma base address).  XPAR_AXI_IIC_MAIN_BASEADDR (the base address of axi_iic_main) is only accessed in XIic_Initialize() --> Xlic_LookupConfig() provided in the BSP, which is NOT used in the bare metal code.  So what is the role of the axi_iic_main in the Linux DRM driver?

I then wondered what the point of the i2c_mixer IP is anyway.  According to the reference design doc: "since axi_iic_main only has a single IIC port, an external multiplexer is required to connect multiple external IIC peripherals. The sys_i2C_mixer multiplexes the IIC ports of both the ADV7511 and the ADAU1761 back to the axi_iic_main IIC controller in the Zynq PL."  But I thought that I2C is NOT a 1-1 protocol like SPI!?

Hallelujah, Vivado 2013.4 generated bitstream works!

While googling what other people had to say about ADV7511 I2C communication, I stumbled on a forum question that said Vivado 2013.4 generated bitstream works for the Linaro Ubuntu demo, but Vivado 2014.2 generated bitstream does NOT, even though there are no obvious errors in connections between IPs.  So I downloaded 2013.4 to generate a bitstream, and created a BOOT.bin that has the same FSBL and uboot.elf, and beheld the 2 penguins on my monitor for the first time!

I then backed up the working 2013.4 folder, and fired up Vivado 2014.2 again, to let the auto-upgrade perform its magic, and got only 1 warning during the process:
 [IP_Flow 19-3298] Detected external port differences while upgrading IP 'system_sys_ps7_0'. These changes may impact your design.
In the upgrade log for IP 'system_sys_ps7_0', the above warning is elaborated: ports TTC0_CLKx_IN were removed.  But as you can see in the HW design connection for the PS7, this project does NOT use those ports anyway (TTC0_CLKx_IN are disconnected), so we are OK!
The updated HW design also works like the 2013.4 bitstream.

Back to the Qt example

With a working HW, back to the Qt demo application pathstroke.

# /usr/lib/qt/examples/widgets/painting/pathstroke/pathstroke
-plugin evdevmouse:/dev/input/event0 -plugin evdevkeyboard:/dev/input/event1



Zedboard HDMI output can drive demo application, and I can move the cursor!

BUT the keyboard is NOT responding, because the application does not use the keyboard.  When I tried the same arguments with the widgets/desktop/screenshot application, I saw that Qt was handling keyboard input.  I think the application is also not choosing the screen resolution correctly...

TODO: running QT application as non-root

The default /dev/fb0 permission (root:root 600) is too restrictive.  So far, I have been using the "dynamic using devtmpfs only" /dev management option in Buildroot, but reading the buildroot documentation, there doesn't seem to be a way to change the permission of devtmpfs files.  Instead, to change the permission of the device file /dev/fb0 through buildroo, there seem to be 2 ways:
  • Static using device table
  • Dynamic using mdev
  • There is actually also the eudev option (which is like the udev in the desktop world), but that requires rebuilding the toolchain with large file, so I don't want to consider it for now

If using static device table, Buildroot provides a way to pass the desired device permission to makedev command: <buildroot>/system/device_table.txt, which has the following format:

# See package/makedevs/README for details
#
# This device table is used to assign proper ownership and permissions
# on various files. It doesn't create any device file, as it is used
# in both static device configurations (where /dev/ is static) and in
# dynamic configurations (where devtmpfs, mdev or udev are used).
#
# <name> <type> <mode> <uid> <gid> <major> <minor> <start> <inc><count>
/dev d 755 0 0 - - - - -
/tmp d 1777 0 0 - - - - -
...
So I should add a new character device entry for /dev/fb0:

# <name> <type> <mode> <uid> <gid> <major> <minor> <start> <inc><count>
/dev/fb0 c 777 0 0 29 0 - - -

To force the root filesystem to rebuild, I deleted the .stamp_target_installed

$ find . -name \.stamp_target_installed -exec rm {} \;

My own Qt5 application

Running an example built by Buildroot is just a quick sanity check that SOME part of the QT infrastructure works.  Now the real work of writing an embedded Qt5 GUI for the Zedboard begins. About 10 years ago, Eclipse was the recommended Qt development environment, but now Eclipse is not officially supported any more; instead QtCreator supports cross-compiling!

Configure Qt Creator for cross compiling for Zed Buildroot

On Ubuntu, QtCreator is available as a Debian package

sudo apt-get install qtcreator

BUT, do NOT go this route; it seems unable to connect to the target!

Instead, get the latest Qt Creator stand-alone installer from the official download page.

$ chmod a+x qt-creator-opensource-linux-x86_64-3.3.2.run
$ ./qt-creator-opensource-linux-x86_64-3.3.2.run

The default extracts Qt Creator at ~/qtcreator-<version>.  I accepted this default.

Configure the target connection

After starting the Qt Creator, go to menu --> Tools --> Options --> Devices --> Add --> Generic Linux Device --> Start Wizard.  I set the connection name as shown below.
The next screen gives you a chance to start the target to be tested.  Boot the target now, and click Finish.  The connection test should succeed, as you can see here:

Configure Build & Run Kit for the target

A Qt Creator kit is a collection of tools and settings necessary to build, run, and debug programs for a given target. I added (menu --> Tools --> Options --> Kits --> Add) the zedbr2 kit with the following settings; unlike for the desktop (host and target are the same) kit, cross compilation requires sysroot, which is where the cross toolchain is hosted on the host system.  For the zedbr2 kit shown above, I also added the following ahead of time, by looking at the Makefile in the Qt analogclock example folder.
  • sysroot: /home/henry/work/zed/buildroot/output/host/usr/arm-buildroot-linux-gnueabi/sysroot
  • Qt version 5.4.0 for Embedded Linux location of qmake: /mnt/work/zed/buildroot/output/build/qtbase-5.4.0/bin/qmake, as you can see below.  Despite the warning (the yellow triangle below) that the qmake is NOT in a deployed folder, things seem to work OK.  More importantly, the cross compile does NOT work if I use the qmake that is in the buildroot/output/host/usr/bin (took me several hours to figure this out!)
  • Compiler path: /mnt/work/zed/buildroot/output/host/usr/bin/arm-buildroot-linux-gnueabi-g++
  • Debugger path: /mnt/work/zed/buildroot/output/host/usr/bin/arm-buildroot-linux-gnueabi-gdb
Do NOT ignore the red error sign next to the kit name if it appears.  A properly setup kit is necessary for even a semi working remote debugging (next section).

Hello world GUI

There are a new Qt books out there; I am reading "Application Development with Qt Creator" by Ray Rischpater at the moment.  I just follow the Hello world GUI example in that book.  Briefly, it puts a label, and a push button whose "click" slot (this is the Qt-terminology for a listener callback) just closes an application, like this:

void MainWindow::on_exitButton_clicked()
{
    QApplication::exit();
}

If the kit has been correctly set up, building the project is a snap.  The executable is emitted to the Qt Creator's workspace (I created mine in ~/work/zed/qt), in a new folder build-<project name>-<kit name>-<build config>.  In this case, the project name is "untitled", the kit name is "zedbr2", and the build config is "Debug", so I found my ELF file in build-untitled-zedbr2-Debug folder, as you can see here:

/mnt/work/zed/qt/build-untitled-zedbr2-Debug$ ls
main.o  mainwindow.o  Makefile  moc_mainwindow.cpp  moc_mainwindow.o  ui_mainwindow.h  untitled

Normally, I expect to click the "bug" button (or F5) to start debugging; the tool should copy the application to the target, start gdbserver for that application on a TCP port, and then make a gdb connection.  Alas, Qt Creator errors out while trying to stop an application.  I worked around by doing the above steps manually:

  1. Copy the ELF file to the target.  Since I run as root, I copied the untitled ELF file above to /root folder.
  2. Start the gdbserver on the target, with the desired argument.  For example (recall from the pathstroke demo earlier that the plugin argument was necessary for the Qt application to recognize the mouse and keyboard inputs):

    gdbserver localhost:1234 /root/untitled -plugin evdevmouse:/dev/input/event0
  3. In QtCreator, attach to the target's gdbserver (menu --> Debug --> Start Debugging --> Attach to Remote Debug Server), specifying the port and the ELF file, as you can see in this example:

Qt Creator debug window will then stop at main (note the "Break at main" checkbox above).  When hit the continue button (F5), I see my simple GUI:

I then set a breakpoint in the button clicked slot, and clicked the "Exit" button above, and behold: the breakpoint is hit!
Hitting F5 will exit the GUI.

Next step: learn Qt

I have avoided GUI all my career, but that's about to change: I signed up for a 4-day Qt training in May!

Appendix: fixing the missing clocks in AXI interfaces in HW design

Vivado keeps complaining about "missing clock in interface", referring to the AXI interfaces with clocks indeed unspecified.  It seems to be a harmless error This forum entry goes into the problem in more depth.