lowRISC with minimal upstream-rebased patches

Gabriel L. Somlo <somlo at cmu dot edu>

1. Prerequisites

This documents the process of building a Linux-capable, RISC-V (rv64imafdc) SoC on a Nexys4-DDR FPGA board, starting from the latest lowRISC project (v0.6), but with all components rebased to upstream, with changes re-organized for maximal legibility and minimal invasiveness against the upstream project. The goal is not to necessarily actually push the changes upstream, but to maintain them in a form that's optimal for either upstream's or third parties' understanding of what's being changed and why. You will need the following:

2. Building Everything from Sources
(a.k.a. "lowRISC rebase-v0.6.+")

Once Vivado is installed and configured in your $PATH, use the following script:
# grab latest sources:
######################
git clone -b refresh-v0.6 \
    https://github.com/lowrisc/lowrisc-chip.git \
    lowrisc-chip-v0.6
cd lowrisc-chip-v0.6

# we only care about the fpga submodule (rolling our own for the rest)
git submodule update --init --recursive fpga


# set environment variables:
source set_env.sh


# gnu-toolchain:
################
git clone --recursive https://github.com/riscv/riscv-gnu-toolchain
pushd riscv-gnu-toolchain
# add rv32ima-ilp32 to list of multilib targets (just because):
sed -i 's/glibc_multilib_names="rv/glibc_multilib_names="rv32ima-ilp32 rv/' \
    configure
./configure --prefix=$HOME/RiscV --enable-multilib

# FIXME: *allegedly* "bare" (newlib) binaries can be compiled with the
# linux/glibc specific build, but in the case of lowRISC that doesn't work with
# the "boot.mem" ROM/firmware blob, so let's build both "linux" and "newlib":
# NOTE: this takes several hours, so you might want to start it within a
# "screen" session and have it email you when finished... :)
make newlib linux
# NOTE: use e.g. CFLAGS="-march=rv32ima -mabi=ilp32" when compiling to arch/abi
# combinations that are NOT rv64gc (the default)!
popd

# lowrisc-chip's set_env.sh already has added ./riscv/bin to $PATH 
# (NOTE: alternatively, "export PATH=$PATH:$HOME/RiscV/bin")
ln -s ../RiscV riscv


# Rocket Verilog (from Chisel sources):
#######################################

# remember, we've "reworded" the lowRISC changes:
rmdir rocket-chip
git clone -b gls-lowrisc-v02 --recursive \
    https://github.com/gsomlo/rocket-chip.git
make -C rocket-chip/vsim verilog CONFIG=LowRiscConfig


# rebuild nexys4ddr bitstream:
##############################

# NOTE: this step relies on a "black-box" proprietary tool suite (Vivado)

pushd fpga/board/nexys4_ddr
patch -p1 < ~/lowrisc-nexys4-rocketsrcupd.patch
make bitstream

# rebuild src/boot.mem (FIXME: only works with bare/newlib compiler for now)
# NOTE: this is the "ROM" (hardware-side bootloader, loads and runs the
#       software bootloader boot.bin from the SDcard)
# NOTE: this actually rebuilds src/boot.mem and then
#       monkey-patches the bitstream file with it !!!
make boot

# copy bitstream file to first FAT partition of the SDcard:
cp lowrisc-chip-imp/lowrisc-chip-imp.runs/impl_1/chip_top.new.bit \
   /mnt/sdcard_p1/chip_top.bit
popd


# build busybox:
################

# NOTE: this rig *can* run Debian and Fedora, but to prove it works, I'm
#       limiting myself to booting the kernel and busybox!
curl https://busybox.net/downloads/busybox-1.29.3.tar.bz2 \
     | tar xfj -
pushd busybox-1.29.3
cp ~/busybox-1.29.3-rv64gc.config .config
make ARCH=rv64imafdc CROSS_COMPILE=riscv64-unknown-linux-gnu-
popd


# build initramfs.cpio:
#######################

# NOTE: kernel in-memory root filesystem
# NOTE: *THIS* is where you'd use a different initramfs.cpio if you wanted
#       to have it pivot to another (Fedora/Debian) root filesystem and load
#       a proper distro. I'm only proving it (still) works, and Busybox is
#       enough for that goal :)

mkdir initramfs
pushd initramfs
mkdir -p bin sbin lib etc dev home proc sys tmp mnt nfs root \
         usr/bin usr/sbin usr/lib
cp ../busybox-1.29.3/busybox bin/
ln -s bin/busybox ./init
cat > etc/inittab <<- "EOT"
::sysinit:/bin/busybox mount -t proc proc /proc
::sysinit:/bin/busybox mount -t tmpfs tmpfs /tmp
::sysinit:/bin/busybox --install -s
/dev/console::sysinit:-/bin/ash
EOT
fakeroot <<- "EOT"
mknod dev/null c 1 3
mknod dev/tty c 5 0
mknod dev/zero c 1 5
mknod dev/console c 5 1
mknod dev/mmcblk0 b 179 0
mknod dev/mmcblk0p1 b 179 1
# mknod dev/mmcblk0p2 b 179 2
find . | cpio -co > ../initramfs.cpio
EOT
popd
rm -rf initramfs


# build kernel:
###############

rmdir linux
git clone https://github.com/gsomlo/linux.git
pushd linux
git checkout gls-lowrisc-v01
make ARCH=riscv lowrisc_defconfig
cp ../initramfs.cpio .
make ARCH=riscv CROSS_COMPILE=riscv64-unknown-linux-gnu-
popd


# build bootloader (bbl):
#########################

# NOTE: this will contain the linux kernel image, which in turn will
#       contain the initramfs root filesystem

git clone https://github.com/gsomlo/riscv-pk.git
pushd riscv-pk
git checkout gls-lowrisc-181105-v01
mkdir build
cd build
../configure --host=riscv64-unknown-linux-gnu \
             --with-payload=$TOP/linux/vmlinux \
             --enable-logo
make bbl

# copy boot.bin to first FAT partition of the SDcard:
cp bbl /mnt/sdcard_p1/
popd
Once you have both chip_top.bit and boot.bin on the SDcard, plug it into your nexys4ddr, and boot lowRISC v0.6 plus the latest-and-greatest versions of RocketChip, BBL, and Kernel!

You may have to eject and re-insert the SDcard after the bitstream is loaded, then hit the CPU-RESET button to allow the "ROM" bootloader to mount it and load boot.bin. For better options check the
lowRISC docs; I'm loading both bitstream and software from the SDcard only because it's most convenient given the idiosyncrasies of my own development environment, and not because I think it's the better way from a technical standpoint :)