$ /james/notes/computers

Building an Embedded Linux OS

The DE10 Nano is a cheap Intel / Altera Cyclone V SoC development board. The board combines a Cyclone V FPGA with a dual core ARM processor.

These instructions show how the build an SD card with the latest mainline u-boot, Linux Kernel, and a Debian root file system.

Prerequisites

A Ubuntu 21.04 system was used for this build, but most Linux systems should work fine.

# For building U-Boot and the Linux kernel
sudo apt install libncurses-dev flex bison openssl \
        libssl-dev dkms libelf-dev libudev-dev libpci-dev \
        libiberty-dev autoconf bc libmpc-dev libgmp-dev
# For debian root filesystem
sudo apt install debootstrap qemu-user-static systemd-container

Create a directory to build everything in.

mkdir -p ~/de10 
cd ~/de10

Get an ARM toolchain

Download the latest stable armebv7-eabihf / glibc toolchain from bootlin.

mkdir toolchain
cd toolchain
wget https://toolchains.bootlin.com/downloads/releases/toolchains/armv7-eabihf/tarballs/armv7-eabihf--glibc--stable-2020.08-1.tar.bz2
tar -xf armv7-eabihf--glibc--stable-2020.08-1.tar.bz2
rm armv7-eabihf--glibc--stable-2020.08-1.tar.bz2
# The CROSS_COMPILE variable must be set in any terminal used to build u-boot or the kernel
export CROSS_COMPILE=$PWD/armv7-eabihf--glibc--stable-2020.08-1/bin/arm-linux-

Build the u-boot bootloader

Make sure CROSS_COMPILE variable is still set in the environment.

cd ~/de10
git clone https://github.com/u-boot/u-boot.git
cd u-boot
# This is the latest version at time of writing
git checkout v2021.04
# Configure the the DE10 nano Intel FPGA SoC
make ARCH=arm socfpga_de10_nano_defconfig
# Optional: run make ARCH=arm menuconfig to have a play around
make ARCH=arm -j 16
# Desired output file is: u-boot/u-boot-with-spl.sfp

The defconfig for DE10-nano is in u-boot/configs/socfpga_de10_nano_defconfig. When this config is set, u-boot/arch/arm/mach-socfpga/Kconfig sets the device to: u-boot/board/terasic/de10-nano/. In u-boot/board/terasic/de10-nano/qts (qts = Quartus) there are 4 files that set ram timing, pin MUXing, and PLL settings. The defaults will work for most applications, but if required they can be changed manually, or can be generated from the BSP handover files generated by the Intel Quartus software. See u-boot/docs/README.socfpga for more info.

Build the Linux kernel

Make sure CROSS_COMPILE variable is still set in the environment.

cd ~/de10
git clone https://github.com/torvalds/linux.git
cd linux
# This is the latest version at time of writing
git checkout v5.12
# Configure kernel
make ARCH=arm socfpga_defconfig
make ARCH=arm menuconfig

Set the following kernel options:

  • Under General setup and uncheck "Automatically append version information to the version string" (optional).
  • Under File systems, enable "Overlay Filesystem Support" and all the options under it.
  • Under File systems, Pseudo File Systems, Enable "Userspace-driven configuration filesystem".

Build the kernel:

# Compile kernel - This will take several minutes
make ARCH=arm LOCALVERSION=zImage -j 16
# Desired output file is: linux/arch/arm/boot/zImage

# Optionally save the config for future use
make savedefconfig
mv defconfig arch/arm/configs/socfpga_de10_defconfig

Make a Debian root filesystem

Generate a skeleton root filesystem

cd ~/de10
mkdir rootfs
sudo debootstrap --arch=armhf --foreign buster rootfs
sudo systemd-nspawn -D ./rootfs/

This will create a container running the generated filesystem. From here the root filesystem can be customised as desired.

Finish setting up Debian

/debootstrap/debootstrap --second-stage

Enable UART TTY

# Add UART TTY out
systemctl enable serial-getty@ttyS0.service
# Set root password
passwd

Set up mount points

nano /etc/fstab

Add the following lines to the file:

tmpfs           /tmp  tmpfs  rw,mode=1777  0   0
/dev/mmcblk0p2  /     ext4   defaults      0   1 

Set locale to en_GB.UTF-8

apt install locales
dpkg-reconfigure locales

Set hostname

This can be set to any value. E.g. de10-nano.

nano /etc/hostname

Set up DHCP

nano /etc/network/interfaces

Add the following under line "source-directory /etc/network/interfaces.d"

auto lo eth0
allow-hotplug eth0
iface lo inet loopback
iface eth0 inet dhcp

Install SSH

Haveged speeds up SSH random number seeding.

apt install openssh-server
apt install haveged
nano /etc/ssh/sshd_config
# Add: PermitRootLogin yes

Install other useful packages

apt install net-tools build-essential device-tree-compiler
apt install python3.7
apt install python3-pip
python3 -m pip install --upgrade pip
python3 -m pip install zmq
python3 -m pip install pyserial

Clean up

pip3 cache purge
apt clean
exit

Compress the root filesystem

cd ~/de10/rootfs
sudo tar -cjpf ../rootfs.tar.bz2 .
cd ..
sudo chown $USER:$USER rootfs.tar.bz2

Create the SD card image

Create a blank image file

Create at least a 1 GB image, and connect it as a loop device.

cd ~/de10
sudo dd if=/dev/zero of=de10_nano_sd.img bs=1G count=1
sudo losetup --show -f de10_nano_sd.img
# Make sure you note the name of the loop device that is used.
# Typically: /dev/loopX where X is a number.

Partition the image

sudo fdisk /dev/loopX

Make the U-Boot and SPL partition:

n <Enter>, p <Enter>, 3 <Enter>, <Enter>, +1M <Enter>, t <Enter>, a2 <Enter>

Make the kernel partition:

n <Enter>, p <Enter>, 1 <Enter>, <Enter>, +254M <Enter>, t <Enter>, b <Enter>

Make the Root Filesystem partition:

n <Enter>, p <Enter>, 2 <Enter>, <Enter>, <Enter>

Check partitions:

p <enter>

Expected output:

Device       Boot  Start     End Sectors  Size Id Type
/dev/loop0p1        4096  524287  520192  254M  b W95 FAT32
/dev/loop0p2      524288 2097151 1572864  768M 83 Linux
/dev/loop0p3        2048    4095    2048    1M a2 unknown

Write the partitions

w <Enter>

It is normal to get error "Re-reading the partition table failed.: Invalid argument". Load the partitions:

sudo partprobe /dev/loopX

ls /dev/loopX* should show:

/dev/loopX  /dev/loopXp1  /dev/loopXp2  /dev/loopXp3

Create the filesystems

sudo mkfs -t vfat /dev/loopXp1
sudo mkfs.ext4 /dev/loopXp2

Copy over U-BOOT

Remember to change /dev/loopX the the right value

cd ~/de10
sudo dd if=./u-boot/u-boot-with-spl.sfp of=/dev/loopXp3 bs=64k seek=0 oflag=sync

Copy Kernel and device tree

cd ~/de10
mkdir fat
sudo mount /dev/loopXp1 fat
sudo cp linux/arch/arm/boot/zImage fat
# Use DE0 DTB for now as no DE10 DTB and they are basically the same board
sudo cp linux/arch/arm/boot/dts/socfpga_cyclone5_de0_nano_soc.dtb fat

Generate extlinux file

cd ~/de10
echo "LABEL Linux Default" > extlinux.conf
echo "    KERNEL ../zImage" >> extlinux.conf
echo "    FDT ../socfpga_cyclone5_de0_nano_soc.dtb" >> extlinux.conf
echo "    APPEND root=/dev/mmcblk0p2 rw rootwait earlyprintk console=ttyS0,115200n8" >> extlinux.conf
sudo mkdir -p fat/extlinux
sudo cp extlinux.conf fat/extlinux
sudo umount fat
rm extlinux.conf

Copy root filesystem

cd ~/de10
mkdir ext4
sudo mount /dev/loopXp2 ext4
cd ext4
sudo tar -xf ../rootfs.tar.bz2
cd ..
sudo umount ext4

Write the image to an SD card

Remember to change /dev/sdX to the right value for your SD card!

cd ~/de10
sudo dd if=de10_nano_sd.img of=/dev/sdX bs=64K status=progress
sync

Pop the SD card into the DE10 nano, and it should boot up into Debian!