a blog

RSS Feed

by Gui Andrade

Bootstrapping Debian onto the PolarFire SoC board

Before I start, the standard disclaimers: I take no responsibility if you break your board, etc., etc.

I was really excited by the possibility of a Debian RISC-V computer where I can even put custom accelerators on its FPGA. Here's how you can do exactly that.

Beware, it's quite an involved process. And there's one particularly unfortunate issue I haven't had the chance to diagnose yet: for some reason, Linux only activates a single core on the processor.

That aside, lots of things work quite well. Including package management! So if you're so inclined to try it out, compile away:

Obligatory demo

This runs on a physical PolarFire Icicle board, and displays over UART1:

Compiling software

Building firmware

# DEPENDENCIES:
# pip install kconfiglib
# riscv64-unknown-linux-gnu-gcc

git clone https://github.com/archshift/hart-software-services
git checkout spi-flash-boot
cp boards/mpfs-icicle-kit-es/def_config .config

# Enable booting from SPI Flash
echo "CONFIG_SERVICE_BOOT_SPI_FLASH=0x400" >> .config

make BOARD=mpfs-icicle-kit-es CROSS_COMPILE=riscv64-unknown-linux-gnu-

Building U-Boot

TODO. I built U-Boot from Microchip's Yocto configuration.

Once you have a u-boot.elf file, do the following:

# Define the payload configuration
cat <<EOT >> ubootcfg.yaml
set-name: 'PolarFire-SoC-HSS::U-Boot'
hart-entry-points: {u54_1: '0x80200000', u54_2: '0x80200000', u54_3: '0x80200000', u54_4: '0x80200000'}
payloads:
  u-boot.elf:    {exec-addr: '0x80200000', owner-hart: u54_1, secondary-hart: u54_2, secondary-hart: u54_3, secondary-hart: u54_4, priv-mode: prv_s}
EOT

# We need this tool to create a bootloader payload
pushd hart-software-services/tools/hss-payload-generator
make
popd

# Create the payload
hart-software-services/tools/hss-payload-generator/hss-payload-generator -c ./ubootcfg.yaml uboot-payload.bin

# Convert the payload to the INTEL HEX format our flashing tool expects
bincopy convert -i binary -o ihex uboot-payload.bin uboot-payload.hex

Building Linux

git clone --depth 1 --branch v5.6.16 https://github.com/gregkh/linux
cd linux
git clone https://github.com/archshift/polarfire-linux buildsh

# Patch the kernel for PolarFire SoC
./buildsh/patch.sh
# Configure the kernel. You can leave the settings as they are and activate "Save".
./buildsh/menuconfig.sh
# Build images
./buildsh/mk-images.sh

Creating disk image

Initialize image

# Initialize disk image
dd if=/dev/zero bs=1M count=2048 of=drive.img
# Partition the disk
fdisk drive.img
> g # make GPT table
> n # new partition, give it +256MB
> t # change partition type, use type 1: EFI system partition
> n # new partition, give it the remaining space
> w # write changes

# Give the disk image loop devices /dev/loop0 (boot partition) and /dev/loop1 (root partition)
sudo losetup -Pf drive.img
# Make the filesystems
sudo mkfs.fat /dev/loop0p1
sudo mkfs.ext4 /dev/loop0p2

Copy boot images

# Mount the partition
mkdir /tmp/boot
sudo mount /dev/loop0p1 /tmp/boot

sudo cp fitImage boot.scr.uimg /tmp/boot

# Unmount
sudo umount /tmp/boot

Debootstrap

# Mount the partition
mkdir /tmp/root
sudo mount /dev/loop0p2 /tmp/root

# Install essential packages
EXTRA_PKGS=ssh,nano,curl,build-essential,debian-ports-archive-keyring,sudo,tmux
sudo debootstrap --foreign --arch riscv64 --include=$EXTRA_PKGS sid /tmp/root http://deb.debian.org/debian-ports/

# Install fstab
echo "/dev/mmcblk0p1         /boot        vfat          defaults         0      2" > /tmp/root/etc/fstab
echo "/dev/mmcblk0p2         /            ext4          defaults         0      1" >> /tmp/root/etc/fstab

# Unmount
sudo umount /tmp/root

Keep configuring with QEMU

# Note that you're going to need the uncompressed kernel image here.
KERNEL=path/to/kernel/arch/riscv/boot/Image
qemu-system-riscv64 -M virt -m 1024 -smp 5 -display none \
    -serial stdio -drive file=drive.img,format=raw,id=hd0 \
    -device virtio-blk-device,drive=hd0 -append "root=/dev/vda2 rw" \
    -kernel "$KERNEL" -netdev user,id=net0 \
    -device virtio-net-device,netdev=net0

Install cached packages

export PATH=/bin:/sbin:/usr/bin:/usr/sbin
# debootstrap doesn't install everything, but it'll download them to the cache. Manually install them now.
dpkg -i -E /var/cache/apt/archives/*.deb
# We'll still have broken packages after this step but the necessary utilities for networking should be installed.

sync
# Reboot now

Final system fixes

# Enable networking
dhclient

# Fix broken packages
apt --fix-broken install
# Do it twice to make sure no stragglers stay unconfigured
apt --fix-broken install

# Ensure all the extra packages we wanted are now installed
apt install ssh nano curl build-essential debian-ports-archive-keyring sudo tmux

# Enable systemd
ln -s /lib/systemd/systemd /sbin/init

# DON'T FORGET TO DO THIS NOW
passwd root

sync
# Close QEMU now

There you go! Now you should have a fully functional drive.img.

Installing

Installing firmware

You will need to copy the firmware payload into the SoC's eNVM flash.

For this you will have the misfortune of using Libero, Microchip's FPGA development platform, or SoftConsole, their firmware development platform. You might be able to also use a JTAG debugger for this, but I'm only familiar with Libero, so that's the tool I'll describe.

Clone the Icicle Kit reference design and open Libero. Click Project->Execute Script, and select ICICLE_KIT_eMMC.tcl under the reference design. This should populate a project.

On the left sidebar, under Program Design, click Generate FPGA Array Data. This will take a long time. Then, Configure Design Initialization Data and Memories. A new view will pop up. Click the eNVM tab.

Click the Add button, and select Add Boot Mode 1 Client. Navigate to the hart-software-services/Default directory from "Building firmware". Select hss.hex. Click OK.

On the left sidebar, under Program Design, click Generate Bitstream. If all goes well, you can then proceed to click Run PROGRAM Action, which will transfer the firmware to your board.

Installing U-Boot

You will need to copy the U-Boot payload into the board's SPI Flash memory at the address 0x400.

Again, for this you will have the misfortune of using Libero. If you have a JTAG debugger, you can avoid Libero, but I'm not familiar with that process.

Assuming you've done everything in "Installing firmware" above, once again click Configure Design Initialization Data and Memories. Navigate to the SPI Flash tab.

First, use the drop-down to reconfigure the SPI Flash to the correct size: 1,048,576 KB. Then click Add->Add Data Storage Client. Select the uboot-payload.hex file from "Building U-Boot."

On the left sidebar, under Program SPI Flash Image, click Generate SPI Flash Image. If all goes well, you can then proceed to click Run PROGRAM_SPI_IMAGE Action, which will transfer the bootloader to your board.

Installing Linux

# Access the boot monitor
sudo screen /dev/ttyUSB0 115200

# Reboot the board, then press any key to stop booting.
# Make the NAND available as a USB device.
> usbdmsc
# Find the block device that corresponds to your NAND flash. It should be 8GB.
sudo fdisk -l
# Copy the NAND image.
sudo dd if=drive.img of=/dev/sdXY