It works every time

... 60% of the time

Building Parallella RISC-V FPGA bitstreams and software with make - Week 4 of GSoC 2016

Make is one of the most common build tools used to write recipes on how you want your software (or hardware) to be built. Although for pure software projects I prefer CMake over Make, for the parallella-riscv port I decided to use the latter since besides building the software we can leverage its power to also generate the FPGA bitstream and everything else needed. Make is a very versatile tool and can be used for all sorts of automation tasks in a project. This post describes some of the make infrastructure that I wrote in order to accomplish the project's build tasks.

Makefile Variables

Each supported board of the project (currently Parallella and ZedBoard - the latter was chosen for development purposes) contains the files it needs in its own directory along with a Makefile to build everything for that board. This makefile contains certain make variables that might be different between board targets and at the end includes a common makefile "fragment" (makefrag) that actually contains all the rules to build everything. For example the Makefile for Parallella is the following:

# ===================
# Parallella Makefile
# ===================


# For Parallella Desktop or MiniServer
# change the BOARD_PART to xc7z010clg400-1
# This will soon happen automatically
# if you change the BOARD_EDITION variable.

BOARD_NAME = parallella

BOARD_EDITION = kickstarter


BOARD_PART = xc7z020clg400-1


# Change the variables below only if you
# need to use a different Linux repo (./boot/REPO)
# or Linux config (config_defconfig) or device tree
# source file (dts/dts_source.dts/dtsi
# to copy on the Linux arch/boot/dts folder)
# or device tree blob target (dtb). See the
# related arm-linux/dts make rules in the
# ./scripts/Makefrag file if you can't
# make sense of the above!

LINUX_REPO = parallella-linux

LINUX_CONFIG = parallella

LINUX_DTS = zynq-parallella1

LINUX_DTB = zynq-parallella-headless

# U-Boot

# Change the variables below only if you want
# to flash Parallella to replace its U-Boot and you
# need to use another U-Boot repo (./boot/REPO)
# or U-Boot config (config_defconfig). Flashing
# your board is not recommended since you may end up
# with a bricked board. The default U-Boot works fine!

UBOOT_REPO = parallella-uboot

UBOOT_CONFIG = adapteva_parallella

# RISC-V Core Arch and Configuration

# Currently only the RV64IMA and RV64IMAFD
# core architectures are supported.
# The default is to use the core without
# the standard F and D extensions (single and
# double floating point support) using an
# optional FPU inside the core.
# In order to use the optional FPU on your
# Parallella Embedded or Kickstarter editions
# change the variables below to RV64IMAFD and
# DefalutFPGAConfig respectively.
# On the Desktop and Miniserver editions that have
# the smaller Zynq FPGA devices the FPU will not fit
# and thus they cannot fit RISC-V cores with the F or D
# extensions.
# Keep in mind that the RV64IMA runs at 50 MHz and the
# RV64IMAFD runs at 25 MHz. So although the latter can
# execute floating point instructions, the former is
# much faster at everything else and thus it is the
# preferred core architecture for all Parallella editions.



# RISC-V DRAM Memory

# The below allocated 384 MB of memory should
# be plenty enough for basic tasks on the
# core. This memory starts from the second 512 MB
# half of the total 1GB memory that Parallella
# provides (the last 32 MB are reserved for the
# Epiphany / E-Link).
# If you need to use more (up to 480 MB)
# change the following (e.g for 480 MB):
# RISCV_DRAM_SIZE_DTS = 0x1e000000
# If you need to use even more memory out of the 1GB
# provided by Parallella it is more tricky and besides
# changing the size to what you need (no more than 736!)
# you should also change the other 3 variables
# to start from a lower DRAM address base e.g 0x10000000
# onRISCV_DRAM_BASE_DTS and set the RTL variables to
# 4\'d1 and 32 - 4 = 28 respectively.



RISCV_DRAM_BASE_DTS = 0x20000000

RISCV_DRAM_SIZE_DTS = 0x18000000


# Include make rules

# Include the rest of the make configuration
# which is common for all boards
include ../scripts/Makefrag

The end-user is not expected to immediately know the meaning or change most of these variables. They should only be changed by someone that attempts to port everything to e.g a new Parallella board or use different RISC-V core setups. All of them have a prefix that depicts which part of the Makefrag file that gets included they are used.

Some of the common top-level make rules that can be used are the following:


Builds the bitstream for the Zynq FPGA device of Parallella. It actually runs Vivado in batch mode using Tcl scripts. The final bitstream file that Parallella's U-Boot can load while booting is created using the bootgen utility (.bit to .bin).


Builds U-Boot, ARM Linux and device tree blob (dtb). U-Boot is not currently used for Parallella since the one contained in its Flash chip is fine and there is always a risk when performing re-flashes. ARM Linux is also identical with the one provided by the latest E-SDK and it's a matter of preference to use the one you build or the fefault from the E-SDK. The device tree blob is necessary in order to use Rocket Core's Host I/O interface and thus communicate with the RISC-V RV64 core.


In order to compile RISC-V programs of your own or the proxy kernel (bbl), the Berkeley bootloader (bbl) and the RISC-V Linux you must build the RISC-V toolchain


This re-generates the RISC-V RV64 core (with IMA or IMAFD standard extensions) from the Chisel sources of UCB's rocket-core generator.


This builds the RISC-V RV64 core emulator (optional).


This builds and copies to the correct output location the RISC-V software needed to communicate and boot applications on the RV64 core on the FPGA. This includes the frontend server (fesvr) which gives access to the core from the host ARM CPUs on Zynq, the proxy kernel (pk) which acts as a basic platform for baremetal-like applications (only basic system calls are supported) and the Berkeley boot loader (bbl) which is capable of loading and booting a full Linux kernel image on the RV64 core.


This builds the latest supported Linux kernel port for the RISC-V architecture.


This rule actually calls a script that builds the RISC-V Poky Linux distribution. The end result is a root image that can be used alongside the vmlinux kernel proper. This image is generated in two formats: an ext2 block device which is the one currently used by the project, or a cpio.gz image for future use as an initramfs, since the new bbl doesn't support disk block devices and the root image must be includedinside the kernel.

Bash Wrapper Scripts

Finally the project contains bash script wrappers that first ensure that the necessary environment variables are set and then call the respective make rules. For example this is the bash script that can build the FPGA bitstream:


SCRIPTS=$(dirname "$(readlink -f "$0")")
source "${SCRIPTS}/"

# Build Bitstream

cd ${TOP}/${BOARD}
make ${BOARD}_bitstream

Github Repository

You can view the project's repository in Github:

Written by Elias Kouskoumvekakis on Monday June 20, 2016

Permalink - Category: news - Tag: gsoc2016

« Automation of Vivado with Tcl - Week 3 of GSoC 2016 - Parallella - RISC-V FPGA design - Week 5 of GSoC 2016 »

comments powered by Disqus