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 some of the key aspects of working with Makefiles 
and explains how you can create Makefiles for your own designs.

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 Symbiflow
commands used by the Makefile to build and compile designs take a look at the
`Understanding Toolchain Commands <understanding-commands.html>`_ page.

Example 
-------

Every example design in Symbiflow has its own Makefile. For example 
`counter test <https://github.com/SymbiFlow/symbiflow-examples/blob/master/xc7/counter_test/Makefile>`_,  
`Linux Litex demo <https://github.com/SymbiFlow/symbiflow-examples/blob/master/xc7/linux_litex_demo/Makefile>`_, 
and `PicoSoC demo <https://github.com/SymbiFlow/symbiflow-examples/blob/master/xc7/picosoc_demo/Makefile>`_ 
all have there own unique Makefiles for compiling and building respective designs. To understand 
how to set up a Makefile in Symbiflow, lets take a look at a simple Makefile. The following code 
is based on the Makefile within `counter test <https://github.com/SymbiFlow/symbiflow-examples/blob/master/xc7/counter_test/Makefile>`_ 
and has been modified slightly for simplicity. Highlighted lines within the code below are of 
particular interest and will change depending on your specific design elements and hardware. 
Lines that are not highlighted do not change from design to design and can be copy and pasted 
into your own Makefile.

.. code-block:: bash
   :name: makefile-example
   :emphasize-lines: 3, 4, 5, 6, 9, 10, 22, 25, 28, 31
   :linenos:

   mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST)))
   current_dir := $(patsubst %/,%,$(dir $(mkfile_path))) 
   TOP := top
   VERILOG := ${current_dir}/counter.v 
   DEVICE  := xc7a50t_test
   BITSTREAM_DEVICE := artix7
   BUILDDIR := build

   PARTNAME := xc7a35tcpg236-1
   XDC := ${current_dir}/basys3.xdc 
   BOARD_BUILDDIR := ${BUILDDIR}/basys3


   .DELETE_ON_ERROR:

   all: ${BOARD_BUILDDIR}/${TOP}.bit

   ${BOARD_BUILDDIR}:
      mkdir -p ${BOARD_BUILDDIR}

   ${BOARD_BUILDDIR}/${TOP}.eblif: | ${BOARD_BUILDDIR}
      cd ${BOARD_BUILDDIR} && symbiflow_synth -t ${TOP} -v ${VERILOG} -d ${BITSTREAM_DEVICE} -p ${PARTNAME} -x ${XDC} 2>&1 > /dev/null

   ${BOARD_BUILDDIR}/${TOP}.net: ${BOARD_BUILDDIR}/${TOP}.eblif
      cd ${BOARD_BUILDDIR} && symbiflow_pack -e ${TOP}.eblif -d ${DEVICE} 2>&1 > /dev/null

   ${BOARD_BUILDDIR}/${TOP}.place: ${BOARD_BUILDDIR}/${TOP}.net
      cd ${BOARD_BUILDDIR} && symbiflow_place -e ${TOP}.eblif -d ${DEVICE} -n ${TOP}.net -P ${PARTNAME} 2>&1 > /dev/null

   ${BOARD_BUILDDIR}/${TOP}.route: ${BOARD_BUILDDIR}/${TOP}.place
      cd ${BOARD_BUILDDIR} && symbiflow_route -e ${TOP}.eblif -d ${DEVICE} 2>&1 > /dev/null

   ${BOARD_BUILDDIR}/${TOP}.fasm: ${BOARD_BUILDDIR}/${TOP}.route
      cd ${BOARD_BUILDDIR} && symbiflow_write_fasm -e ${TOP}.eblif -d ${DEVICE}

   ${BOARD_BUILDDIR}/${TOP}.bit: ${BOARD_BUILDDIR}/${TOP}.fasm
      cd ${BOARD_BUILDDIR} && symbiflow_write_bitstream -d ${BITSTREAM_DEVICE} -f ${TOP}.fasm -p ${PARTNAME} -b ${TOP}.bit

   clean:
      rm -rf ${BUILDDIR}


Adding HDL files to your design 
--------------------------------

:ref:`Line 3 <makefile-example>` 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 change line 3 to 
``TOP := switches``.

.. warning:: 

   If you change the name of your top level module then the command you use to download the bitstream to 
   your board using ``openocd`` will need to change slightly from what is provided in the examples. For 
   instance, if you changed the top level module name to ``TOP := my_module_top`` then the openocd command 
   would change to:

   .. code-block:: bash

      openocd -f <Your install directory>/xc7/conda/envs/xc7/share/openocd/scripts/board/digilent_arty.cfg -c "init; pld load 0 my_module_top.bit; exit"

   Note that the only part of the command that changes is "<top module name>.bit;"

:ref:`Line 4 <makefile-example>` in the Makefile shows how to add HDL files to the design. The general syntax is: 
``<HDL language>:=${current_dir}/<your HDL file path>``. You can also add multiple HDL files to a 
design using the following syntax:
 
 .. code-block:: bash
   :name: multi-file-example

   <HDL language> := ${current_dir}/<HDL file 1> \
                     ${current_dir}/<HDL file 2> \
                     ${current_dir}/<HDL file 3> \
                     ${current_dir}/<HDL file 4> \
                     ...

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 4 in the Makefile with:
 
 .. code-block:: bash
   :name: wildcard-example

    VERILOG := ${current_dir}/*.v

To include SystemVerilog HDL in your designs simply change the ``.v`` extension in the examples 
above to a ``.sv``. You might also want to change the ``VERILOG`` bash variables throughout the 
Makefile to ``SYSTEM_VERILOG`` to improve readability. 

.. note::

   As of this writing, symbiflow 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. 

Setting the Board Type and Part Name
-------------------------------------

:ref:`Line 5 <makefile-example>` in the example Makefile defines the device fabric 
for the board being used in the project. Several different device fabrics are 
supported and a listing of the commands for each follow:

.. tabs::

   .. group-tab:: Arty_35T

      .. code-block:: bash
         :name: example-counter-a35t-group

         DEVICE := xc7a50t_test

   .. group-tab:: Arty_100T

      .. code-block:: bash
         :name: example-counter-a100t-group

         DEVICE := xc7a100t_test

   .. group-tab:: Nexus 4 DDR

      .. code-block:: bash
         :name: example-counter-nexys4ddr-group

         DEVICE := xc7a100t_test

   .. group-tab:: Basys3

      .. code-block:: bash
         :name: example-counter-basys3-group

         DEVICE := xc7a50t_test

   .. group-tab:: Zybo Z7

      .. code-block:: bash
         :name: example-counter-zybo-group

         DEVICE := xc7z010_test

   .. group-tab:: Nexys Video

      .. code-block:: bash
         :name: example-counter-nexys_video-group

         DEVICE := xc7a200t_test

:ref:`Line 7 <makefile-example>` defines the family for your FPGA. For example basys3 and arty boards are from the artix7
family while zybo boards are from the zynq7 series.

As shown on :ref:`line 9 <makefile-example>` of the example Makefile, you will also need to define the specific FPGA part 
number for your chip. To do this, you need to add the following line of code to your Makefile 
depending on your hardware:

.. tabs::

   .. group-tab:: Arty_35T

      .. code-block:: bash
         :name: example-part-a35t-group

         PARTNAME := xc7a35tcsg324-1

   .. group-tab:: Arty_100T

      .. code-block:: bash
         :name: example-part-a100t-group

         PARTNAME := xc7a100tcsg324-1

   .. group-tab:: Nexus 4 DDR

      .. code-block:: bash
         :name: example-part-nexys4ddr-group

         PARTNAME := xc7a100tcsg324-1

   .. group-tab:: Basys3

      .. code-block:: bash
         :name: example-part-basys3-group

         PARTNAME := xc7a35tcpg236-1

   .. group-tab:: Zybo Z7

      .. code-block:: bash
         :name: example-part-zybo-group

         PARTNAME := xc7z010clg400-1

   .. group-tab:: Nexys Video

      .. code-block:: bash
         :name: example-part-nexys_video-group

         PARTNAME := xc7a200tsbg484-1


Constraint files
----------------

:ref:`Line 10 <makefile-example>` shows 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:

.. tabs::

   .. group-tab:: XDC
   
      .. code-block:: bash

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

   .. group-tab:: SDC+PCF

         .. code-block:: bash

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

Note that the :ref:`lines 22, 25, 28, and 31 <makefile-example>` (.eblif, net, place, and route) will also need to change 
depending on if you use an XDC file or some combination of SDC and PCF files. The following 
snippets show the differences and the areas that will need to change:

.. tabs::

   .. group-tab:: XDC

      .. code-block:: bash
         :lineno-start: 21
         :emphasize-lines: 2

         ${BOARD_BUILDDIR}/${TOP}.eblif: | ${BOARD_BUILDDIR}
            cd ${BOARD_BUILDDIR} && symbiflow_synth -t ${TOP} -v ${VERILOG} -d ${BITSTREAM_DEVICE} -p ${PARTNAME} -x ${XDC} 2>&1 > /dev/null

         ${BOARD_BUILDDIR}/${TOP}.net: ${BOARD_BUILDDIR}/${TOP}.eblif
            cd ${BOARD_BUILDDIR} && symbiflow_pack -e ${TOP}.eblif -d ${DEVICE} 2>&1 > /dev/null

         ${BOARD_BUILDDIR}/${TOP}.place: ${BOARD_BUILDDIR}/${TOP}.net
            cd ${BOARD_BUILDDIR} && symbiflow_place -e ${TOP}.eblif -d ${DEVICE} -n ${TOP}.net -P ${PARTNAME} 2>&1 > /dev/null

         ${BOARD_BUILDDIR}/${TOP}.route: ${BOARD_BUILDDIR}/${TOP}.place
            cd ${BOARD_BUILDDIR} && symbiflow_route -e ${TOP}.eblif -d ${DEVICE} 2>&1 > /dev/null

   .. group-tab:: SDC+PCF

      .. code-block:: bash
         :lineno-start: 21
         :emphasize-lines: 5, 8, 11

         ${BOARD_BUILDDIR}/${TOP}.eblif: | ${BOARD_BUILDDIR}
            cd ${BOARD_BUILDDIR} && symbiflow_synth -t ${TOP} -v ${VERILOG} -d ${BITSTREAM_DEVICE} -p ${PARTNAME}
 
         ${BOARD_BUILDDIR}/${TOP}.net: ${BOARD_BUILDDIR}/${TOP}.eblif
            cd ${BOARD_BUILDDIR} && symbiflow_pack -e ${TOP}.eblif -d ${DEVICE} -s ${SDC}
      
         ${BOARD_BUILDDIR}/${TOP}.place: ${BOARD_BUILDDIR}/${TOP}.net
            cd ${BOARD_BUILDDIR} && symbiflow_place -e ${TOP}.eblif -d ${DEVICE} -p ${PCF} -n ${TOP}.net -P ${PARTNAME} -s ${SDC} 2>&1 > /dev/null
         
         ${BOARD_BUILDDIR}/${TOP}.route: ${BOARD_BUILDDIR}/${TOP}.place
            cd ${BOARD_BUILDDIR} && symbiflow_route -e ${TOP}.eblif -d ${DEVICE} -s ${SDC} 2>&1 > /dev/null
         

   
:ref:`Lines 33-37 <makefile-example>` (running ``symbiflow_write_fasm`` and ``symbiflow_write_bitstream``) typically do 
not change within the Makefile from design to design. 

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

If you look at the Makefiles from the example designs within Symbiflow 
(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 <https://github.com/SymbiFlow/symbiflow-examples/blob/master/xc7/counter_test/Makefile>`_:


.. code-block:: bash
   :name: counter-test Makefile snippet
   :lineno-start: 9

   ifeq ($(TARGET),arty_35)
   PARTNAME := xc7a35tcsg324-1
   XDC:=${current_dir}/arty.xdc
   BOARD_BUILDDIR := ${BUILDDIR}/arty_35
   else ifeq ($(TARGET),arty_100)
   PARTNAME := xc7a100tcsg324-1
   XDC:=${current_dir}/arty.xdc
   DEVICE := xc7a100t_test
   BOARD_BUILDDIR := ${BUILDDIR}/arty_100
   else ifeq ($(TARGET),nexys4ddr)
   PARTNAME:= xc7a100tcsg324-1
   XDC:=${current_dir}/nexys4ddr.xdc
   DEVICE := xc7a100t_test
   BOARD_BUILDDIR := ${BUILDDIR}/nexys4ddr
   else ifeq ($(TARGET),zybo)
   PARTNAME := xc7z010clg400-1
   XDC := ${current_dir}/zybo.xdc
   DEVICE := xc7z010_test
   BITSTREAM_DEVICE := zynq7
   BOARD_BUILDDIR := ${BUILDDIR}/zybo
   VERILOG := ${current_dir}/counter_zynq.v
   else ifeq ($(TARGET),nexys_video)
   PARTNAME := xc7a200tsbg484-1
   XDC := ${current_dir}/nexys_video.xdc
   DEVICE := xc7a200t_test
   BOARD_BUILDDIR := ${BUILDDIR}/nexys_video
   else
   PARTNAME := xc7a35tcpg236-1
   XDC := ${current_dir}/basys3.xdc
   BOARD_BUILDDIR := ${BUILDDIR}/basys3
   endif

This snippet of code is an if else block used to set the specific PARTNAME and DEVICE parameters 
for different types of hardware. Since each FPGA has a unique pin configuration, the block also 
defines a constraint file specific to the hardware being used (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, then you could just use something similar to :ref:`lines 5, 9 and 10 <makefile-example>` in our example:

.. code-block:: bash
   :name: device-partname-snippet

   DEVICE := xc7a50t_test

   PARTNAME := xc7a35tcpg236-1
   XDC := ${current_dir}/<name of XDC file>

If you plan on using multiple types of hardware for your designs, then it might be better to just 
copy the if else block from one of the Symbiflow-examples. Note that you may need to change the 
names for the XDC or PCF+SDC parameters to match the names you have used. Also remember that you 
will need to set the TARGET variable before running make on your design.