Ultra96 board with a Grove IoT Mezzanine board mounted on top. The Grove board provided the UART through a serial-to-USB connector.
This board is produced by Xilinx. There is a great resource for hardware documentation over at 96boards.
Like other famous deities, the Ultra96 board is known under many names. It is also known as "Zynqmp ZCU100 rev C".
Getting a Cross Compiler for AArch64
For compiling the U-Boot, kernel and root filesystem you can just use the latest Linaro AArch64 GCC toolchain I'm using the Aarch64 7.2.1 for Linux as of writing.Building the upstream kernel
Here are some files that I use to configure and build Ultra96 also known as ZCU100 revision C kernels out-of-the box. These use a minimal initramfs root filesystem placed in your home directory. (This is to get you to a prompt without a root filesystem installed in the flash, such as the ArchLinux method described below.) These root filesystems are created using this script. Prebuilt versions can be obtained here:
- ultra96.mak can be used to compile the Aarch64 multiplatform kernel.
- rootfs-aarch64.cpio CPIO archive with initramfs root filesystem for Aarch64 machines
Copy the rootfs to you home directory and the ultra96.mak file to your linux git tree or base dir and simply:
> make -f ultra96.mak config > make -f ultra96.mak build
Pre-built kernels
Here are a bunch of pre-built kernels with initramfs that can be used to test that the Ultra96 board is working. They should give prompt on the serial console and a heartbeat LED on the Ultra96 board itself (blinks between the USB host connectors). See below for boot methods.
- Image-v4.17-rc7 - first thing I ever booted.
Boot over serial port using Xmodem
An exteremely rudimentary boot method is to boot the kernel over serial port. (In this case the emulated serial port that you access on USB-to-serial.) As the board comes with U-Boot you can load the kernel to 0x10000000 and the DTB to 0x11800000.
If you can't get your homecooked kernel to boot, verify that you can boot one of my kernels, see below for downloads.
Get to the U-Boot prompt:
- Connect a USB micro cable to the Mezzanine UART adapter (any version)
- Start minicom on 115200n8 on /dev/ttyUSB0 or similar USB serial port you're using
- Power on the board
- Hammer ENTER to interrupt U-Boot to command line
when it comes up. It should look like this:
ZynqMP> - ZynqMP> loady 0x10000000
- Select the Image file from the menu in minicom then wait for the file to download to the target. The kernel is big, so this is going to take time.
- ZynqMP> loady 0x11800000
- Select the zynqmp-zcu100-revC.dtb device tree binary from the menu in minicom and wait for the file to download to the target. This should be pretty quick.
- booti 0x10000000 - 0x11800000
- The kernel should boot up.
Boot from SD Card
The default boot of the Ultra96 board is from the SD card. The images that will be automatically booted comes from the /boot partition on the device, which is partition 1 on the MMC card /dev/mmcblk0p1 in a machine with a proper SD card reader, if you have a USB-based SD card reader it may appear as /dev/sdN1 instead.
- Insert the card into a card reader on your development host and make sure the boot partition mounts.
- Copy the kernel image and DTB binary over, assuming you have
these in your home directory:
cp ~/Image /<path-to-boot-partition> cp ~/zynqmp-zcu100-revC.dtb /<path-to-boot-partition> umount /<path-to-boot-partition>
- Connect a USB micro cable to the Mezzanine UART adapter (any version)
- Start minicom on 115200n8 on /dev/ttyUSB0 or similar USB serial port you're using
- Power on the board
- Hammer ENTER to interrupt U-Boot to command line
when it comes up. It should look like this:
ZynqMP> - printenv: you should familiarize yourself with how U-Boot loads image.ub and boots it from the card in the default U-Boot configuration: bootcmd runs default_bootcmd and that loads the U-Boot environment from the SD card, then loads the kernel image on a U-Boot format and boots it using bootm. We are NOT going to do things that way!
- setenv raw_bootcmd "mmcinfo && fatload mmc 0 0x10000000 Image && fatload mmc 0 0x11800000 zynqmp-zcu100-revC.dtb && booti 0x10000000 - 0x11800000"
- printenv and assure yourself that raw_bootcmd now exist and looks as it should
- saveenv - the new raw_bootcmd gets saved to the SD card and available in all subsequent boots
- You can now issue this manually by typing run raw_bootcmd and the homebrewn Image and Zynq DTB file should load fine from the boot partition and start.
- If you also want to make this the power-on default instead of having
to type run raw_bootcmd every time you start the system from
and SD card, type:
setenv bootcmd run raw_bootcmd saveenv
- After this the homebrewn Image and .dtb will boot off the card all the time.
Install and boot ArchLinux
I made an ArchLinux ARM installation based on the generic Aarch64. I do this using a USB stick with Aarch64 archlinux on it. This means we need to mount the rootfs from /dev/sda1 and pass the right command line to the kernel to do this. I just compile root=/dev/sda1 rw rootwait into the kernel using CONFIG_CMDLINE.
- Apply this patch for v4.17-rc7: this fixes the command line args to boot from the first SDA1 partition on a USB stick, and sets down the speed on the USB block so that it works properly.
- Take a blank USB stick or hard disk or whatever USB storage you have.
- Plug the USB stick into your host computer.
- Unmount any partitions that automount under /run/media/*
- fdisk /dev/sdX
- Type o. This will clear out any partitions on the drive.
- Type p to list partitions. There should be no partitions left.
- Type n, then p for primary, 1 for the first partition on the drive, press ENTER to accept the default first sector, then enter again for the last sector so we use all the storage.
- fdisk may ask you whether you really want to do this... you do.
- Write the partition table and exit by typing w
- mkfs.ext4 /dev/sdX1
- cd /tmp
- wget http://os.archlinuxarm.org/os/ArchLinuxARM-aarch64-latest.tar.gz (this file is big)
- mkdir /mnt/rootfs && mount /dev/sdX1 /mnt/rootfs
- cd /mnt/rootfs
- tar xvfz /tmp/ArchLinuxARM-aarch64-latest.tar.gz
if tar complains about magic keywords like SCHILY.fflags I choose to just ignore that, I have no idea of what it means even. - cd
- umount /mnt/rootfs (this may take a while)
- Insert the USB stick into one of the Ultra96 host ports
- Update your SD card on the Ultra96 with a kernel that is modified to boot from the USB /dev/sda1 partition.
- Start with U-Boot as usual (see above)
Here is first bootlog for ArchLinuxARM on the Ultra96 board as reference. Systemd and everything works like a charm.
If you have trouble compiling your kernel for ArchLinuxARM or applying the specific DTS patches here are my binary snapshots, these need to be renamed to Image and zynqmp-zcu100-revC.dtb respectively and then places on the SD card as described above:
- Image-v4.17-rc7-archlinuxarm - a v4.17-rc7 kernel image configured to boot ArchLinuxARM from USB
- zynqmp-zcu100-revC-archlinuxarm.dtb the hacked DTB file resulting from patching the DTS with the patch mentioned above
After booting
- Login with root/root to get to the root account.
- (TBD: figure out a way to network the card... WiFi doesn't seem to work...)
- If the network does not come up, type dhcpcd
- If your network is up you can upgrade the newly booted system with pacman -Syu
- Change root and alarm passwords
- hostnamectl set-hostname Ultra96
- systemctl enable dhcpcd.service
- systemctl start dhcpcd.service