Customizing the Makefiles

A powerful tool in creating your own designs is understanding how to generate your own Makefile to compile projects. This tutorial walks you through how to do that.

If you would like to use methods other than a Makefile to build and compile your designs (such as python or bash scripts) or if you would like to learn more about the various F4PGA commands used by the common Makefile to build and compile designs take a look at the F4PGA Documentation page.


By including F4PGA’s provided common Makefile in your designs, running the commands necessary for building your personal projects is incredibly simple. All you have to do is run a few simple commands and set a few variables.

Create a makefile for your project by running touch Makefile, and add the following to the contents.

 1current_dir := ${CURDIR}
 2TOP := <put the name of your top module here>
 3SOURCES := ${current_dir}/<put your HDL sources here>
 5# Include your constraint file path(s) below. Use either an XDC file
 6# or a PCF+SDC pair. Don't use all three file types.
 7XDC := ${current_dir}/<name of your pcf file if applicable>
 8PCF := ${current_dir}/<name of your xdc file if applicable>
 9SDC := ${current_dir}/<name of your sdc file if applicable>
11include <path to f4pga-examples root directory>/common/

Lets talk briefly about each of the commands in the above makefile

Adding HDL Sources and Specifying the Top Module

Line 2 in the Makefile shows how to define the name for your top level module. For example, if your top module was named module switches ( ... then you would simply uncomment line 3 and change the text in <> to TOP := switches.

Line 3 in the Makefile shows how to add HDL files to the design. The general syntax is: SOURCES:=${current_dir}/<your HDL file path>. You can also add multiple HDL files to a design using the following syntax:

SOURCES := ${current_dir}/<HDL file 1> \
       ${current_dir}/<HDL file 2> \
       ${current_dir}/<HDL file 3> \
       ${current_dir}/<HDL file n> \

You could also use wildcards to collect all HDL file types of a specific extension and add them to your design. For example, if you wanted to add all verilog files within the current directory to your design, you could replace line 3 in the Makefile with:

 SOURCES := ${current_dir}/*.v

To include SystemVerilog HDL in your designs simply change the .v extension in the examples above to a .sv.


As of this writing, F4PGA only offers full support for Verilog by default. SystemVerilog can also be run through the toolchain but more complicated designs may not be fully supported.

Constraint files

Lines 7-9 show how you can specify what constraint files are being used for your design. The general syntax depends on whether you are using XDC files or a SDC+PCF pair:

XDC := ${current_dir}/<name of XDC file>


Line 1 calls a make function CURDIR which returns the absolute path for the current directory. Line 9 simply includes the path to the common makefile.

A Note on the example designs use of ifeq/else ifeq blocks

If you look at the Makefiles from the example designs within F4PGA (i.e. counter test, Picosoc, etc.), you will find an ifeq else ifeq block. The following snippet is from lines 9-39 of the Makefile from counter test:

 5ifeq ($(TARGET),arty_35)
 6   XDC := ${current_dir}/arty.xdc
 7else ifeq ($(TARGET),arty_100)
 8   XDC := ${current_dir}/arty.xdc
 9else ifeq ($(TARGET),nexys4ddr)
10   XDC := ${current_dir}/nexys4ddr.xdc
11else ifeq ($(TARGET),zybo)
12   XDC := ${current_dir}/zybo.xdc
13   SOURCES:=${current_dir}/counter_zynq.v
14else ifeq ($(TARGET),nexys_video)
15   XDC := ${current_dir}/nexys_video.xdc
17   XDC := ${current_dir}/basys3.xdc

This snippet of code is an if else block used to set device specific constraints (i.e. basys3.xdc, nexys_video.xdc). The code block determines what type of hardware is being used based upon a TARGET variable which is assumed to be defined before running make. For example, you may recall running TARGET="<board type>" make -C counter_test before building the counter test example. This command sets the TARGET variable to the type of hardware you are using.

The if else block is completely optional. If you are only using one type of hardware for your designs you could just specify the TARGET variable within your makefile like so:

1current_dir := ${CURDIR}
2TARGET := basys3
3TOP := ${current_dir}/# put the name of your top module here
4SOURCES := ${current_dir}/# put your HDL sources here

By setting the TARGET variable within the Makefile itself, you don’t even have to specify the TARGET variable before calling make. You can just use make -C <path to directory containing your design>