diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 21b3a3c4bbefb604f149f9f30d2f0d695cd2ba14..c2fa9abe329270b6eb8450b734283426728a30f2 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -9,7 +9,7 @@ test:rd53:
       - bash miniconda.sh -b -p $HOME/miniconda
       - export PATH=$HOME/miniconda/bin:$PATH
       - conda update --yes conda
-      - conda install --yes numpy bitarray pytest pyyaml scipy numba pytables pyqt matplotlib tqdm pyzmq blosc psutil pexpect coloredlogs
+      - conda install --yes numpy bitarray pytest pyyaml scipy numba pytables pyqt matplotlib tqdm pyzmq blosc psutil pexpect coloredlogs ptyprocess
       # Setup co-simulation
       - pip install cocotb==1.0.dev3
       - git clone -b development --depth 1 https://github.com/SiLab-Bonn/basil.git; cd basil; python setup.py develop; cd ..;
@@ -48,36 +48,43 @@ test:software:
       # - if [ -z "$CI_COMMIT_TAG"]; then git clone -b v3.0.0 --depth 1 https://github.com/SiLab-Bonn/basil.git; cd basil; python setup.py develop; cd ..; else git clone -b development --depth 1 https://github.com/SiLab-Bonn/basil.git; cd basil; python setup.py develop; cd ..; fi
     script:
       - python setup.py develop
-      - cd bdaq53/tests
+      # Try to activate conda evironment in the newest broken miniconda docker
+      - conda init bash  # https://github.com/ContinuumIO/docker-images/issues/89
+      - source ~/.bashrc  # since ==> For changes to take effect, close and re-open your current shell. <==
+      - conda activate  # to link properly to pytest
       # Do not run virtual x server tests (monitor) in this runner due to segfault
-      - pytest -v test_software --ignore=test_software/test_monitor.py
+      - pytest -v bdaq53/tests/test_software --ignore=bdaq53/tests/test_software/test_monitor.py
 
 # Tests that need bdaq53 readout hardware and RD53 FE
+# Needs Ubuntu system with xvfb, Miniconda3 and full Xilinx installed and
+# Gitlab-runner with shell executor: https://docs.gitlab.com/runner/executors/
 test:hardware:
     allow_failure: true
     tags:  # tags to differentiate runners able to run the job
       - hardware  # Use Silab hardware runner
-    image: continuumio/miniconda3:latest  # Ubuntu based miniconda image
     before_script:
-      # Update miniconda python and install required binary packages
-      - conda update --yes conda
-      - conda install --yes numpy bitarray pytest pyyaml scipy numba pytables pyqt matplotlib tqdm pyzmq blosc psutil pexpect coloredlogs
+      # Install required binary python packages
+      - export PATH=$HOME/miniconda/bin:$PATH
+      - conda install --yes numpy bitarray pytest pyyaml scipy numba pytables pyqt matplotlib tqdm pyzmq blosc psutil pexpect coloredlogs ptyprocess
       # Install virtual x server for matplotlib Qt error
-      - apt-get update
-      - apt-get install -y xvfb
       - pip install xvfbwrapper
-      # Install basil from github
-      - git clone -b development --depth 1 https://github.com/SiLab-Bonn/basil.git; cd basil; python setup.py develop; cd ..;
-      # - if [ -z "$CI_COMMIT_TAG"]; then git clone -b v3.0.0 --depth 1 https://github.com/SiLab-Bonn/basil.git; cd basil; python setup.py develop; cd ..; else git clone -b development --depth 1 https://github.com/SiLab-Bonn/basil.git; cd basil; python setup.py develop; cd ..; fi
+      # Update basil development from github
+      - git --git-dir=$HOME/git/basil/.git pull
+      # Clean SiTCP git checkout from potential preceding runner run  
+      - rm -rf firmware/SiTCP/{*,.git}
+      - mkdir firmware/SiTCP
+      - touch firmware/SiTCP/.gitkeep  # recreate gitkeep file
     script:
       - python setup.py develop
-      # FIXME: This should work
-      # - bdaq53 --firmware BDAQ53  # Flash newest development firmware
-      - cd bdaq53/tests
-      - pytest -v test_hardware
+      # Compile firmware if changes detected and flash
+      - if [[ `git diff ..development -- firmware` ]]; then bdaq53 --firmware BDAQ53_RX640 -c; bdaq53 --firmware BDAQ53_RX640.bit; else bdaq53 --firmware BDAQ53_RX640; fi
+      - pytest -v bdaq53/tests/test_hardware
     artifacts:
+      when: always  # also upload verilog log on failure
       paths:
+        - "firmware/vivado/vivado.log"
         - "output_data/*.pdf"
+        - "BDAQ53_RX640.bit"
       expire_in: 1 month
 
 # Tests for code style violations in new code lines
diff --git a/bdaq53/bdaq53.py b/bdaq53/bdaq53.py
index 438ece2b66787ad9a297fa046dbe110475e50eaf..b55cd8622faf145a25efb9e8bd9551c4d86a2f1b 100644
--- a/bdaq53/bdaq53.py
+++ b/bdaq53/bdaq53.py
@@ -12,12 +12,8 @@ import time
 import struct
 import coloredlogs
 import numpy as np
-import tables as tb
-
-from tables.exceptions import NoSuchNodeError
 
 from basil.dut import Dut
-from basil.utils.BitLogic import BitLogic
 
 import pkg_resources
 VERSION = pkg_resources.get_distribution("bdaq53").version
diff --git a/bdaq53/bdaq53_cli.py b/bdaq53/bdaq53_cli.py
index 7879fe330043645903a2bc9e679985873231acfa..35a0eec9c8f13e04329ee0cfdbe7b563360361f4 100644
--- a/bdaq53/bdaq53_cli.py
+++ b/bdaq53/bdaq53_cli.py
@@ -12,7 +12,7 @@ import yaml
 
 from importlib import import_module
 from inspect import getmembers
-from bdaq53 import firmware_downloader
+from bdaq53 import firmware_manager
 
 
 logger = logging.getLogger('BDAQ53')
@@ -68,10 +68,14 @@ def main():
                         nargs=1,
                         help='Path of vivado installation',)
 
+    parser.add_argument('-c', '--compile',
+                        action='store_true',
+                        help='Compile firmware',)
+
     args = parser.parse_args()
 
     if args.firmware is not None:
-        firmware_downloader.main(args.firmware[0], path=args.vivado_path[0])
+        firmware_manager.main(args.firmware[0], path=args.vivado_path[0], create=args.compile)
 
     if args.scan:
         mod = import_module('bdaq53.scans.' + args.scan)
diff --git a/bdaq53/firmware_downloader.py b/bdaq53/firmware_downloader.py
deleted file mode 100644
index f8b766c4a897aa2009928d36fe89ce676264472e..0000000000000000000000000000000000000000
--- a/bdaq53/firmware_downloader.py
+++ /dev/null
@@ -1,257 +0,0 @@
-#
-# ------------------------------------------------------------
-# Copyright (c) All rights reserved
-# SiLab, Institute of Physics, University of Bonn
-# ------------------------------------------------------------
-#
-
-import urllib
-import re
-import logging
-import requests
-import math
-import pkg_resources
-import tarfile
-import os.path
-from sys import platform
-from tqdm import tqdm
-import pexpect
-
-import bdaq53
-
-repository = r'https://gitlab.cern.ch/silab/bdaq53/'
-
-firmware_dev_url = repository + r'wikis/Hardware/Firmware-(development-versions)'
-firmware_url = repository + r'tags'
-
-logger = logging.getLogger(__name__)
-logger.setLevel(logging.INFO)
-
-
-def get_web_page(url):
-    ''' Read web page.
-    '''
-    response = urllib.request.urlopen(url)  # Add md for markdown
-    return response.read()
-
-
-def download_firmware(name, url):
-    ''' Download firmware tar.gz file from url
-
-        The release firmwares are stored with the tag information (firmware_url)
-        and the development firmware is attached to a wiki page (firmware_dev_url)
-    '''
-    response = get_web_page(url)
-    links = re.findall(r'\[%s\]\((.*?)\)' % name, response)
-    links = [repository + 'wikis/' + ''.join(link) for link in links]
-
-    if not links:
-        all_firmwares = re.findall(r'%s(.*?)%s' % (r'/uploads/\w+/', '.tar.gz'), response)
-        logging.error('No firmware with name %s at %s\nPossible firmwares: %s', name, url, ', '.join(all_firmwares))
-        return
-    if len(links) > 1:
-        raise RuntimeError('Found multiple firmwares with name %s at %s: %s', name, url, str(links))
-    download_link = links[0]
-    logging.info('Downloading %s', download_link)
-    # Streaming, so we can iterate over the response.
-    r = requests.get(download_link, stream=True)
-
-    # Total size in bytes.
-    total_size = int(r.headers.get('content-length', 0))
-    block_size = 1024
-    wrote = 0
-    with open(name, 'wb') as f:
-        for data in tqdm(r.iter_content(block_size), total=math.ceil(total_size // block_size), unit='KB', unit_scale=True):
-            wrote = wrote + len(data)
-            f.write(data)
-    if total_size != 0 and wrote != total_size:
-        raise RuntimeError('Download failed!')
-    return name
-
-
-def unpack_bit_file(name):
-    ''' Extracts the bit file from the tar.gz file
-
-        name: string
-            Name of bit file (.tar.gz suffix) and
-            Name of compressed bitfile (name.bit)
-    '''
-    tar = tarfile.open(name, "r:gz")
-    name_no_suffix = name[:name.find('.')]
-    for m in tar.getmembers():
-        if name_no_suffix + '.bit' in m.name:
-            m.name = os.path.basename(m.name)  # unpack without folder
-            tar.extract(m, path=".")
-            logging.debug('Unpacked %s', m.name)
-    tar.close()
-    return name_no_suffix + '.bit'
-
-
-def flash_firmware(name):
-    ''' Flash firmware using vivado in tcl mode
-    '''
-
-    def get_return_string(timeout=1):
-        ''' Helper function to get full return string.
-
-            More complexity needed here since Xilinx does multi line returns
-        '''
-        flushed = ''
-        try:
-            while not vivado.expect(r'.+', timeout=timeout):
-                flushed += vivado.match.group(0)
-        except pexpect.exceptions.TIMEOUT:
-            pass
-        return flushed
-
-    logger.info('Flash firmware %s', name)
-
-    try:
-        vivado = pexpect.spawn('vivado_lab -mode tcl', timeout=10)  # try lab version
-        vivado.expect('Vivado', timeout=5)
-    except pexpect.exceptions.ExceptionPexpect:
-        try:
-            vivado = pexpect.spawn('vivado -mode tcl', timeout=10)  # try full version
-            vivado.expect('Vivado', timeout=5)
-        except pexpect.exceptions.ExceptionPexpect:
-            logger.error('Cannot execute vivado / vivado_lab commend')
-            return
-    vivado.expect(['vivado_lab%', 'Vivado%'])  # Booted up when showing prompt
-
-    vivado.sendline('open_hw')
-    vivado.expect(['vivado_lab%', 'Vivado%'])  # Command finished when showing prompt
-
-    vivado.sendline('connect_hw_server')
-    vivado.expect('localhost')  # Printed when successfull
-    get_return_string()
-
-    vivado.sendline('current_hw_target')
-    ret = get_return_string()
-    if 'WARNING' in ret:
-        logger.error('Cannot find programmer hardware, Xilinx warning:\n%s', ret)
-        vivado.sendline('exit')
-        vivado.expect('Exiting')
-        return
-
-    vivado.sendline('open_hw_target')
-    vivado.expect('Opening hw_target')  # Printed when programmer found
-
-    vivado.sendline('current_hw_device [lindex [get_hw_devices] 0]')
-    vivado.expect(['vivado_lab%', 'Vivado%'])  # Printed when finished
-
-    vivado.sendline('set devPart [get_property PART [current_hw_device]]')
-    vivado.expect(['vivado_lab%', 'Vivado%'])  # Printed when finished
-
-    vivado.sendline('set_property PROGRAM.FILE {%s} [current_hw_device]' % name)
-    vivado.expect(['vivado_lab%', 'Vivado%'])  # Printed when finished
-
-    vivado.sendline('program_hw_devices [current_hw_device]')
-    vivado.expect('End of startup status: HIGH')  # firmware upload successfull
-
-    vivado.sendline('exit')
-    vivado.expect('Exiting')
-
-    logger.info('SUCCESS!')
-
-
-def find_vivado(path):
-    ''' Search in std. installation paths for vivado(_lab) binary
-    '''
-
-    if platform == "linux" or platform == "linux2":
-        linux_install_path = '/opt/' if not path else path
-        # Try vivado full install
-        paths = where(name='vivado', path=linux_install_path)
-        for path in paths:
-            if 'bin' in path:
-                return os.path.dirname(os.path.realpath(path))
-        # Try vivado lab install
-        paths = where(name='vivado_lab', path=linux_install_path)
-        for path in paths:
-            if 'bin' in path:
-                return os.path.dirname(os.path.realpath(path))
-    else:
-        raise NotImplementedError('Only Linux supported thus far')
-
-
-def where(name, path, flags=os.F_OK):
-    result = []
-    paths = [path]
-    for outerpath in paths:
-        for innerpath, _, _ in os.walk(outerpath):
-            path = os.path.join(innerpath, name)
-            if os.access(path, flags):
-                result.append(os.path.normpath(path))
-    return result
-
-
-def main(name, path=None):
-    ''' All steps to upload matching firmware to FPGA
-
-        If name has .bit suffix try to flash from local file
-        If not available find suitable firmware online, download, extract, and  flash
-    '''
-
-    vivado_path = find_vivado(path)
-    if vivado_path:
-        logger.debug('Found vivado binary at %s', vivado_path)
-        os.environ["PATH"] += os.pathsep + vivado_path
-    else:
-        if path:
-            logger.error('Cannot find vivado installation in %s', path)
-        else:
-            logger.error('Cannot find vivado installation! Install vivado lab from here:\nhttps://www.xilinx.com/support/download.html')
-        return
-
-    if os.path.isfile(name):
-        logger.info('Found existing local bit file')
-        bit_file = name
-    else:
-        if not name.endswith('.tar.gz'):
-            name += '.tar.gz'
-        stable_firmware = True  # std. setting: use stable (tag) firmware
-        version = pkg_resources.get_distribution("bdaq53").version
-        try:
-            import git
-            try:
-                bdaq53_path = os.path.dirname(bdaq53.__file__)
-                repo = git.Repo(search_parent_directories=True, path=bdaq53_path)
-                active_branch = repo.active_branch
-                if active_branch != 'master':
-                    stable_firmware = False  # use development firmware
-            except git.InvalidGitRepositoryError:  # no github repo --> use stable firmware
-                pass
-        except ImportError:  # git not available
-            logger.warning(
-                'Git not properly installed, assume software release %s', version)
-            pass
-        if stable_firmware:
-            tag_list = get_tag_list(firmware_url)
-            matches = [i for i in range(len(tag_list)) if version in tag_list[i]]
-            if not matches:
-                raise RuntimeError('Cannot find tag version %s at %s', version, firmware_url)
-            tag_url = firmware_url + '/' + tag_list[matches[0]]
-            logger.info('Download stable firmware version %s', version)
-            archiv_name = download_firmware(name, tag_url)
-        else:
-            logger.info('Download development firmware')
-            archiv_name = download_firmware(name, firmware_dev_url + '.md')
-        if not archiv_name:
-            return
-        bit_file = unpack_bit_file(archiv_name)
-    flash_firmware(bit_file)
-
-
-def get_tag_list(url):
-    ''' Extracts all tag names from firmware_url
-
-        This is needed since the naming scheme is inconsistent
-    '''
-
-    response = get_web_page(url)
-    return re.findall(r'href="/silab/bdaq53/tags/(.*?)"', response)
-
-
-if __name__ == '__main__':
-    logging.basicConfig(level=logging.DEBUG)
-    main('BDAQ53_RX640')
diff --git a/bdaq53/firmware_manager.py b/bdaq53/firmware_manager.py
new file mode 100644
index 0000000000000000000000000000000000000000..3279664e4a571c76b895c77236260525e515b003
--- /dev/null
+++ b/bdaq53/firmware_manager.py
@@ -0,0 +1,407 @@
+#
+# ------------------------------------------------------------
+# Copyright (c) All rights reserved
+# SiLab, Institute of Physics, University of Bonn
+# ------------------------------------------------------------
+#
+
+''' Module to manage firmware download, compilation and flashing using vivado.
+    Mainly for CI runner but also useful during headless session (e.g. test beams)
+'''
+
+import urllib
+import re
+import logging
+import requests
+import math
+import fileinput
+import pkg_resources
+import tarfile
+import shutil
+import os.path
+from sys import platform
+
+import pexpect
+from tqdm import tqdm
+import git
+
+import bdaq53
+
+logger = logging.getLogger(__name__)
+logger.setLevel(logging.INFO)
+
+repository = r'https://gitlab.cern.ch/silab/bdaq53/'
+firmware_dev_url = repository + r'wikis/Hardware/Firmware-(development-versions)'
+firmware_url = repository + r'tags'
+
+sitcp_repo = r'https://github.com/BeeBeansTechnologies/SiTCP_Netlist_for_Kintex7'
+
+bdaq53_path = os.path.dirname(bdaq53.__file__)
+
+
+def get_web_page(url):
+    ''' Read web page.
+    '''
+    response = urllib.request.urlopen(url)  # Add md for markdown
+    return response.read().decode('utf-8')
+
+
+def download_firmware(name, url):
+    ''' Download firmware tar.gz file from url
+
+        The release firmwares are stored with the tag information (firmware_url)
+        and the development firmware is attached to a wiki page (firmware_dev_url)
+    '''
+    response = get_web_page(url)
+    links = re.findall(r'\[%s\]\((.*?)\)' % name, response)
+    links = [repository + 'wikis/' + ''.join(link) for link in links]
+
+    if not links:
+        all_firmwares = re.findall(r'%s(.*?)%s' % (r'/uploads/\w+/', '.tar.gz'), response)
+        logging.error('No firmware with name %s at %s\nPossible firmwares: %s', name, url, ', '.join(all_firmwares))
+        return
+    if len(links) > 1:
+        raise RuntimeError('Found multiple firmwares with name %s at %s: %s', name, url, str(links))
+    download_link = links[0]
+    logging.info('Downloading %s', download_link)
+    # Streaming, so we can iterate over the response.
+    r = requests.get(download_link, stream=True)
+
+    # Total size in bytes.
+    total_size = int(r.headers.get('content-length', 0))
+    block_size = 1024
+    wrote = 0
+    with open(name, 'wb') as f:
+        for data in tqdm(r.iter_content(block_size), total=math.ceil(total_size // block_size), unit='KB', unit_scale=True):
+            wrote = wrote + len(data)
+            f.write(data)
+    if total_size != 0 and wrote != total_size:
+        raise RuntimeError('Download failed!')
+    return name
+
+
+def unpack_bit_file(name):
+    ''' Extracts the bit file from the tar.gz file
+
+        name: string
+            Name of bit file (.tar.gz suffix) and
+            Name of compressed bitfile (name.bit)
+    '''
+    tar = tarfile.open(name, "r:gz")
+    name_no_suffix = name[:name.find('.')]
+    for m in tar.getmembers():
+        if name_no_suffix + '.bit' in m.name:
+            m.name = os.path.basename(m.name)  # unpack without folder
+            tar.extract(m, path=".")
+            logging.debug('Unpacked %s', m.name)
+    tar.close()
+    return name_no_suffix + '.bit'
+
+
+def flash_firmware(name):
+    ''' Flash firmware using vivado in tcl mode
+    '''
+
+    def get_return_string(timeout=1):
+        ''' Helper function to get full return string.
+
+            This complexity needed here since Xilinx does multi line returns
+        '''
+        flushed = bytearray()
+        try:
+            while not vivado.expect(r'.+', timeout=timeout):
+                flushed += vivado.match.group(0)
+        except pexpect.exceptions.TIMEOUT:
+            pass
+        return flushed.decode('utf-8')
+
+    logger.info('Flash firmware %s', name)
+
+    try:
+        vivado = pexpect.spawn('vivado_lab -mode tcl', timeout=10)  # try lab version
+        vivado.expect('Vivado', timeout=5)
+    except pexpect.exceptions.ExceptionPexpect:
+        try:
+            vivado = pexpect.spawn('vivado -mode tcl', timeout=10)  # try full version
+            vivado.expect('Vivado', timeout=5)
+        except pexpect.exceptions.ExceptionPexpect:
+            logger.error('Cannot execute vivado / vivado_lab commend')
+            return
+    vivado.expect(['vivado_lab%', 'Vivado%'])  # Booted up when showing prompt
+
+    vivado.sendline('open_hw')
+    vivado.expect(['vivado_lab%', 'Vivado%'])  # Command finished when showing prompt
+
+    vivado.sendline('connect_hw_server')
+    vivado.expect('localhost')  # Printed when successfull
+    get_return_string()
+
+    vivado.sendline('current_hw_target')
+    ret = get_return_string()
+    if 'WARNING' in ret:
+        logger.error('Cannot find programmer hardware, Xilinx warning:\n%s', ret)
+        vivado.sendline('exit')
+        vivado.expect('Exiting')
+        return
+
+    vivado.sendline('open_hw_target')
+    vivado.expect('Opening hw_target')  # Printed when programmer found
+
+    vivado.sendline('current_hw_device [lindex [get_hw_devices] 0]')
+    vivado.expect(['vivado_lab%', 'Vivado%'])  # Printed when finished
+
+    vivado.sendline('set devPart [get_property PART [current_hw_device]]')
+    vivado.expect(['vivado_lab%', 'Vivado%'])  # Printed when finished
+
+    vivado.sendline('set_property PROGRAM.FILE {%s} [current_hw_device]' % name)
+    vivado.expect(['vivado_lab%', 'Vivado%'])  # Printed when finished
+
+    vivado.sendline('program_hw_devices [current_hw_device]')
+    vivado.expect('End of startup status: HIGH')  # firmware upload successfull
+
+    vivado.sendline('exit')
+    vivado.expect('Exiting')
+
+    logger.info('SUCCESS!')
+
+
+def compile_firmware(name):
+    ''' Compile firmware using vivado in tcl mode
+    '''
+
+    def get_return_string(timeout=1):
+        ''' Helper function to get full return string.
+
+            This complexity needed here since Xilinx does multi line returns
+        '''
+        flushed = bytearray()
+        try:
+            while not vivado.expect(r'.+', timeout=timeout):
+                flushed += vivado.match.group(0)
+        except (pexpect.exceptions.TIMEOUT, pexpect.exceptions.EOF):
+            pass
+        return flushed.decode('utf-8')
+
+    supported_firmwares = ['BDAQ53', 'USBPIX3', 'KC705',
+                           'BDAQ53_RX640', 'USBPIX3_RX640', 'KC705_RX640']
+    if name not in supported_firmwares:
+        logger.error('Can only compile firmwares: %s', ','.join(supported_firmwares))
+        return
+
+    logger.info('Compile firmware %s', name)
+
+    vivado_tcl = os.path.join(bdaq53_path, '..', 'firmware/vivado')
+
+    # Use mappings from run.tcl
+    fpga_types = {'BDAQ53': 'xc7k160tffg676-2',
+                  'USBPIX3': 'xc7k160tfbg676-1',
+                  'KC705': 'xc7k325tffg900-2'}
+    constrains_files = {'BDAQ53': '../src/bdaq53.xdc',
+                        'USBPIX3': '../src/usbpix3.xdc',
+                        'KC705': '../src/kc705_gmii.xdc'}
+    flash_sizes = {'BDAQ53': '64',
+                   'USBPIX3': '64',
+                   'KC705': '16'}
+
+    for k, v in fpga_types.items():
+        if k in name:
+            fpga_type = v
+            constrain_files = constrains_files[k]
+            flash_size = flash_sizes[k]
+            board_name = k
+
+    if '_SMA' in name:
+        connector = '_SMA'
+    elif '_FMC_LPC' in name:
+        connector = '_FMC_LPC'
+    else:
+        connector = '""'
+
+    if 'RX640' in name:
+        option = '_RX640'
+    else:
+        option = '""'
+
+    command_args = fpga_type + ' ' + board_name + ' ' + connector + ' ' + constrain_files + ' ' + flash_size + ' ' + option
+    command = 'vivado -mode tcl -source run.tcl -tclargs %s' % command_args
+    logger.info('Compiling firmware. Takes about 10 minutes!')
+    try:
+        vivado = pexpect.spawn(command, cwd=vivado_tcl, timeout=10)
+        vivado.expect('Vivado', timeout=5)
+    except pexpect.exceptions.ExceptionPexpect:
+        logger.error('Cannot execute vivado command %d.\nMaybe paid version is missing, that is needed for compilation?', command)
+        return
+
+    import time
+    timeout = 10  # 50 seconds with no new print to screen
+    t = 0
+    while t < timeout:
+        r = get_return_string()
+        if r:
+            if 'write_cfgmem completed successfully' in r:
+                break
+            print('.', end='', flush=True)
+            t = 0
+        else:
+            time.sleep(5)
+            t += 1
+    else:
+        raise RuntimeError('Timeout during compilation, check vivado.log')
+
+    # Move firmware to current folder
+    cwd = os.getcwd()
+    vivado_tcl = os.path.join(bdaq53_path, '..', 'firmware/vivado')
+    shutil.move(os.path.join(vivado_tcl, r'output/%s' % name + '.bit'), cwd)
+    logger.info('SUCCESS!')
+
+
+def find_vivado(path):
+    ''' Search in std. installation paths for vivado(_lab) binary
+    '''
+
+    if platform == "linux" or platform == "linux2":
+        linux_install_path = '/opt/' if not path else path
+        # Try vivado full install
+        paths = where(name='vivado', path=linux_install_path)
+        for path in paths:
+            if 'bin' in path:
+                return os.path.dirname(os.path.realpath(path))
+        # Try vivado lab install
+        paths = where(name='vivado_lab', path=linux_install_path)
+        for path in paths:
+            if 'bin' in path:
+                return os.path.dirname(os.path.realpath(path))
+    else:
+        raise NotImplementedError('Only Linux supported')
+
+
+def where(name, path, flags=os.F_OK):
+    result = []
+    paths = [path]
+    for outerpath in paths:
+        for innerpath, _, _ in os.walk(outerpath):
+            path = os.path.join(innerpath, name)
+            if os.access(path, flags):
+                result.append(os.path.normpath(path))
+    return result
+
+
+def main(name, path=None, create=False):
+    ''' Steps to download/compile/flash matching firmware to FPGA
+
+        name: str
+            Firmware name:
+                If name has .bit suffix try to flash from local file
+                If not available find suitable firmware online, download, extract, and  flash
+        compile: boolean
+            Compile firmware
+    '''
+
+    vivado_path = find_vivado(path)
+    if vivado_path:
+        logger.debug('Found vivado binary at %s', vivado_path)
+        os.environ["PATH"] += os.pathsep + vivado_path
+    else:
+        if path:
+            logger.error('Cannot find vivado installation in %s', path)
+        else:
+            logger.error('Cannot find vivado installation!')
+            if not create:
+                logger.error('Install vivado lab from here:\nhttps://www.xilinx.com/support/download.html')
+            else:
+                logger.error('Install vivado paid version to be able to compile firmware')
+        return
+
+    if not create:
+        if os.path.isfile(name):
+            logger.info('Found existing local bit file')
+            bit_file = name
+        else:
+            if not name.endswith('.tar.gz'):
+                name += '.tar.gz'
+            stable_firmware = True  # std. setting: use stable (tag) firmware
+            version = pkg_resources.get_distribution("bdaq53").version
+            if not os.getenv('CI'):
+                try:
+                    import git
+                    try:
+                        repo = git.Repo(search_parent_directories=True, path=bdaq53_path)
+                        active_branch = repo.active_branch
+                        if active_branch != 'master':
+                            stable_firmware = False  # use development firmware
+                    except git.InvalidGitRepositoryError:  # no github repo --> use stable firmware
+                        pass
+                except ImportError:  # git not available
+                    logger.warning('Git not properly installed, assume software release %s', version)
+                    pass
+                if stable_firmware:
+                    tag_list = get_tag_list(firmware_url)
+                    matches = [i for i in range(len(tag_list)) if version in tag_list[i]]
+                    if not matches:
+                        raise RuntimeError('Cannot find tag version %s at %s', version, firmware_url)
+                    tag_url = firmware_url + '/' + tag_list[matches[0]]
+                    logger.info('Download stable firmware version %s', version)
+                    archiv_name = download_firmware(name, tag_url)
+                else:
+                    logger.info('Download development firmware')
+                    archiv_name = download_firmware(name, firmware_dev_url + '.md')
+            else:  # always use development version for CI runner
+                archiv_name = download_firmware(name, firmware_dev_url + '.md')
+            if not archiv_name:
+                return
+            bit_file = unpack_bit_file(archiv_name)
+        flash_firmware(bit_file)
+    else:
+        get_si_tcp()  # get missing SiTCP sources
+        compile_firmware(name)
+
+
+def get_tag_list(url):
+    ''' Extracts all tag names from firmware_url
+
+        This is needed since the naming scheme is inconsistent
+    '''
+
+    response = get_web_page(url)
+    return re.findall(r'href="/silab/bdaq53/tags/(.*?)"', response)
+
+
+def get_si_tcp():
+    ''' Download SiTCP sources from official github repo and apply patches
+    '''
+
+    def line_prepender(filename, line):
+        with open(filename, 'rb+') as f:
+            content = f.read()
+            f.seek(0, 0)
+            # Python 3, wtf?
+            add = bytearray()
+            add.extend(map(ord, line))
+            add.extend(map(ord, '\n'))
+            f.write(add + content)
+
+    sitcp_folder = os.path.join(bdaq53_path, '..', 'firmware/SiTCP/')
+
+    # Only download if not already existing SiTCP git repository
+    if not os.path.isdir(os.path.join(sitcp_folder, '.git')):
+        logger.info('Downloading SiTCP')
+
+        # Has to be moved to be allowed to use existing folder for git checkout
+        shutil.move(sitcp_folder + '.gitkeep', os.path.join(sitcp_folder, '..'))
+        git.Repo.clone_from(url=sitcp_repo,
+                            to_path=sitcp_folder, branch='master')
+        shutil.move(os.path.join(sitcp_folder, '..', '.gitkeep'), sitcp_folder)
+        # Patch sources, see README of bdaq53
+        line_prepender(filename=sitcp_folder + 'TIMER.v', line=r'`default_nettype wire')
+        line_prepender(filename=sitcp_folder + 'WRAP_SiTCP_GMII_XC7K_32K.V', line=r'`default_nettype wire')
+        for line in fileinput.input([sitcp_folder + 'WRAP_SiTCP_GMII_XC7K_32K.V'], inplace=True):
+            print(line.replace("assign\tMY_IP_ADDR[31:0]\t= (~FORCE_DEFAULTn | (EXT_IP_ADDR[31:0]==32'd0) \t? DEFAULT_IP_ADDR[31:0]\t\t: EXT_IP_ADDR[31:0]\t\t);",
+                               'assign\tMY_IP_ADDR[31:0]\t= EXT_IP_ADDR[31:0];'), end='')
+    else:  # update if existing
+        g = git.cmd.Git(sitcp_folder)
+        g.pull()
+
+
+if __name__ == '__main__':
+    logging.basicConfig(level=logging.DEBUG)
+    main('BDAQ53_RX640')
diff --git a/bdaq53/periphery.py b/bdaq53/periphery.py
index 4b1b8d1103fb3d52aa959ae023d86cedb6834d3c..6c847347f20cac78b697d8fd150439dfebb8f828 100644
--- a/bdaq53/periphery.py
+++ b/bdaq53/periphery.py
@@ -61,7 +61,7 @@ class BDAQ53Periphery(object):
             return
         try:
             self.devices.init()
-            self.logger.debug('Initialized SCC powersupply %s' % (self.devices['SCC_Powersupply'].get_info()))
+            self.logger.debug('Initialized SCC powersupply %s' % (self.devices['SCC_Powersupply'].get_name()))
             try:
                 self.logger.debug('Initialized sensor bias powersupply %s' % (self.devices['SensorBias'].get_name()))
             except KeyError:
diff --git a/bdaq53/periphery.yaml b/bdaq53/periphery.yaml
index a75382c681325304e800295a248f782dc265216f..8001eec008d3ac6ce7bb756df43543e034cdc9db 100644
--- a/bdaq53/periphery.yaml
+++ b/bdaq53/periphery.yaml
@@ -8,7 +8,7 @@ transfer_layer:
   - name     : Serial
     type     : Serial
     init     :
-        port     : /dev/ttyACM0
+        port     : /dev/ttyUSB0
         read_termination : "\r\n"
         write_termination : "\n"
         baudrate : 19200
diff --git a/bdaq53/rd53a.py b/bdaq53/rd53a.py
index 6208641eeae1dae1c8c2d050a39717d4c43c699d..5f92282a306bd04e6c4ed9316d2d4f7c616252f0 100644
--- a/bdaq53/rd53a.py
+++ b/bdaq53/rd53a.py
@@ -310,8 +310,7 @@ class RD53A(object):
         self.reset_masks()
 
     def init(self, **kwargs):
-        self.set_aurora(**kwargs)
-        self.bdaq.wait_for_aurora_sync()
+        self.init_communication(**kwargs)
         self.write_ecr()
 
     def set_aurora(self, tx_lanes=1, CB_Wait=255, CB_Send=1, chip_id=8, only_cb=False, write=True, **_):
@@ -379,6 +378,39 @@ class RD53A(object):
             self.write_command(indata)
         return indata
 
+    def init_communication(self, **kwargs):
+        logger.info('Initializing communication...')
+
+        # Configure cmd encoder
+        self.bdaq['cmd'].reset()
+        time.sleep(0.1)
+        self.write_command(self.write_sync(write=False) * 32)
+        # Wait for PLL lock
+        self.bdaq.wait_for_pll_lock()
+
+        aurora_data = self.set_aurora(**kwargs)
+
+        # Workaround for problems locking
+        for _ in range(30):
+            self.write_command(self.write_sync(write=False) * 32)
+            self.write_ecr()
+
+            self.write_command(aurora_data)
+            time.sleep(0.01)
+            try:
+                self.bdaq.wait_for_aurora_sync()
+            except RuntimeError:
+                pass
+            else:
+                break
+
+            self.write_command([0x00] * 1000, repetitions=1)
+            time.sleep(0.01)
+        else:
+            self.bdaq.wait_for_aurora_sync()
+
+        logger.success('Communication established')
+
     def write_global_pulse(self, width, chip_id=8, write=True):
         # 0101_1100    ChipId<3:0>,0    Width<3:0>,0
         indata = [self.CMD_GLOBAL_PULSE] * 2  # [0b01011100]
diff --git a/bdaq53/tests/.gitignore b/bdaq53/tests/.gitignore
index 92b911c569e9bb0b15ebc9f6d0ebf1576fff2356..0b8273d328299650095dfc433636261c5d5210af 100644
--- a/bdaq53/tests/.gitignore
+++ b/bdaq53/tests/.gitignore
@@ -6,7 +6,11 @@ unisims
 secureip
 /cds.lib
 
-/sim_build
+/test_rd53/sim_build
+/test_rd53/output_data
+/test_rd53/*.log
+/test_rd53/Makefile
+
 /results.xml
 /*.gtkw
 /simvision#
diff --git a/bdaq53/tests/setup_eudaq.sh b/bdaq53/tests/setup_eudaq.sh
index 5125d18113621bf4ad01e5d0b9c52e632fd1c141..14c9e318d98592d168c5828df84dae81919aab54 100644
--- a/bdaq53/tests/setup_eudaq.sh
+++ b/bdaq53/tests/setup_eudaq.sh
@@ -1,4 +1,4 @@
-git clone -b v1.7-dev https://github.com/eudaq/eudaq
+git clone -b v1.x-dev https://github.com/eudaq/eudaq
 cd eudaq/build
 cmake -DBUILD_python=ON -DBUILD_gui=OFF -DBUILD_onlinemon=OFF -DBUILD_runsplitter=OFF ..
 make -j 4
diff --git a/bdaq53/tests/test_hardware/test_scan_scripts.py b/bdaq53/tests/test_hardware/test_scan_scripts.py
index b8ba281366f0467e455f79aa251e9dcb707e424f..c0119b955d108b592756ca3cecfff8f7df7b2af6 100644
--- a/bdaq53/tests/test_hardware/test_scan_scripts.py
+++ b/bdaq53/tests/test_hardware/test_scan_scripts.py
@@ -5,39 +5,16 @@
 # ------------------------------------------------------------
 #
 
-import os
-import time
 import logging
 import unittest
 
 import tables as tb
 import numpy as np
-from slackclient import SlackClient
 
 logger = logging.getLogger(__file__)
 
 
 class TestScanScripts(unittest.TestCase):
-    @classmethod
-    def setUpClass(cls):
-        if os.getenv('CI', False):
-            job_id = os.environ.get('CI_JOB_ID', 'unknown')
-            text = 'Use setup for hardware based test job #%s in 5 minutes! Visit https://gitlab.cern.ch/silab/bdaq53/-/jobs/%s to cancel.' % (job_id, job_id)
-            logger.info(text)
-            if os.getenv('SLACK_TOKEN', False):
-                cls.slack = SlackClient(os.environ.get('SLACK_TOKEN'))
-                if not cls.slack.api_call('chat.postMessage', channel='rd53a_module_setup', text=text, username='BDAQ53 Bot', icon_emoji=':robot_face:')['ok']:
-                    raise unittest.SkipTest("Cannot use Slack to inform users about hardware usage. Skip hardware based test!")
-            else:
-                raise unittest.SkipTest("Cannot get Slack credentials to inform users about hardware usage. Skip hardware based test!")
-            time.sleep(60 * 5)
-            cls.slack.api_call('chat.postMessage', channel='rd53a_module_setup', text='Starting...', username='BDAQ53 Bot', icon_emoji=':robot_face:')
-
-    @classmethod
-    def tearDownClass(cls):
-        if os.getenv('CI', False):
-            cls.slack.api_call('chat.postMessage', channel='rd53a_module_setup', text='Done!', username='BDAQ53 Bot', icon_emoji=':robot_face:')
-
     def test_digital_scan(self):
         ''' Test digital scan '''
         from bdaq53.scans import scan_digital
@@ -64,15 +41,49 @@ class TestScanScripts(unittest.TestCase):
         scan.close()
 
         with tb.open_file(scan.output_filename + '_interpreted.h5') as in_file:
-            # 60 % of the pixels see all injections, since FE issues
             logger.error(np.count_nonzero((in_file.root.HistOcc[:].sum(axis=2) != 100)))
-            self.assertTrue(np.count_nonzero(in_file.root.HistOcc[:].sum(axis=2) == 100) > 0.6 * 400 * 192)
+            self.assertTrue(np.count_nonzero(in_file.root.HistOcc[:].sum(axis=2) == 100) > 0.99 * 400 * 192)
             self.assertTrue(np.any(in_file.root.HistRelBCID[:]))
             self.assertTrue(np.any(in_file.root.HistTot[:]))
             # We expect BCID errors from SYNC
             # self.assertTrue(np.all(in_file.root.HistBCIDError[:] == 0))
             # self.assertTrue(np.all(in_file.root.HistEventStatus[:] == 0))
 
+    def test_threshold_scan(self):
+        ''' Test threshold scan and results '''
+        from bdaq53.scans import scan_threshold
+        scan = scan_threshold.ThresholdScan()
+        scan.start(**scan_threshold.local_configuration)
+        scan.close()
+
+#         with tb.open_file(scan.output_filename + '_interpreted.h5') as in_file:
+#             logger.error(np.count_nonzero((in_file.root.HistOcc[:].sum(axis=2) != 100)))
+#             self.assertTrue(np.count_nonzero(in_file.root.HistOcc[:].sum(axis=2) == 100) > 0.99 * 400 * 192)
+#             self.assertTrue(np.any(in_file.root.HistRelBCID[:]))
+#             self.assertTrue(np.any(in_file.root.HistTot[:]))
+#             # We expect BCID errors from SYNC
+#             # self.assertTrue(np.all(in_file.root.HistBCIDError[:] == 0))
+#             # self.assertTrue(np.all(in_file.root.HistEventStatus[:] == 0))
+
+#     def test_fast_threshold_scan(self):
+#         ''' Test fast threshold scan and results '''
+#         from bdaq53.scans import scan_threshold_fast
+#         scan = scan_threshold_fast.FastThresholdScan()
+#         scan.start(**scan_threshold_fast.local_configuration)
+#         scan.close()
+#
+#     def test_register_test(self):
+#         from bdaq53.scans import test_registers
+#         scan = test_registers.RegisterTest()
+#         scan.start(**test_registers.local_configuration)
+#         scan.close()
+#
+#     def test_threshold_noise_tuning(self):
+#         from bdaq53.scans import tune_local_threshold_noise
+#         scan = tune_local_threshold_noise.NoiseTuning()
+#         scan.start(**tune_local_threshold_noise.local_configuration)
+#         scan.close()
+
 
 if __name__ == '__main__':
     unittest.main()
diff --git a/firmware/SiTCP/.gitkeep b/firmware/SiTCP/.gitkeep
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/firmware/src/bdaq53_core.v b/firmware/src/bdaq53_core.v
index c12af14f6c793520b3eff07b866f41e1a5fab89d..dd2d88ecfe3b15d4e4a6784a2bd61ebc1e9350c2 100755
--- a/firmware/src/bdaq53_core.v
+++ b/firmware/src/bdaq53_core.v
@@ -37,7 +37,6 @@
 `include "utils/rgmii_io.v"
 `include "utils/rbcp_to_bus.v"
 `include "utils/fifo_32_to_8.v"
-`include "utils/clock_divider.v"
 `include "utils/bus_to_ip.v"
 `include "utils/ddr_des.v"
 `include "rrp_arbiter/rrp_arbiter.v"
@@ -169,7 +168,7 @@ localparam BDAQ53 = 16'd1;
 localparam USBPIX3 = 16'd2;
 localparam KC705 = 16'd3;
 
-`ifdef COCOTB_SIM
+`ifdef RTL_SIM
     localparam BOARD = SIM;
     localparam CONNECTOR = CON_SMA;
 `elsif BDAQ53
diff --git a/firmware/src/rx_aurora/rx_aurora_64b66b_1lane/ip/exdes/aurora_64b66b_1lane_exdes.v b/firmware/src/rx_aurora/rx_aurora_64b66b_1lane/ip/exdes/aurora_64b66b_1lane_exdes.v
index 094dae930e6fe000bb2ed739851b48b003f7d340..ba9cf0e2651db62a2a630ecfa55b887e24deb635 100644
--- a/firmware/src/rx_aurora/rx_aurora_64b66b_1lane/ip/exdes/aurora_64b66b_1lane_exdes.v
+++ b/firmware/src/rx_aurora/rx_aurora_64b66b_1lane/ip/exdes/aurora_64b66b_1lane_exdes.v
@@ -68,47 +68,46 @@
 // This is sample simplex exdes file
 `timescale 1 ns / 10 ps
 
-`define SIM_ENCRYPTED
-
-`ifndef SYNTHESIS
-    `include "rx_aurora_64b66b_1lane/ip/exdes/aurora_64b66b_1lane_cdc_sync_exdes.v"
-    `ifdef SIM_ENCRYPTED    // use the encrypted ip core for the GitLab CI
-        `include "rx_aurora_64b66b_1lane/ip/sim/core.vp"
-    `else   // otherwise we use the non-encrypted version
-
-        `include "rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane.v"
-        `include "rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane_core.v"
-
-        `include "rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_64b66b_descrambler.v"
-        `include "rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_block_sync_sm.v"
-        `include "rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_cbcc_gtx_6466.v"
-        `include "rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_cdc_sync.v"
-        `include "rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_common_logic_cbcc.v"
-        `include "rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_common_reset_cbcc.v"
-        `include "rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_ll_to_axi.v"
-        `include "rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_reset_logic.v"
-        `include "rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_rx_aurora_lane_simplex.v"
-        `include "rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_rx_channel_err_detect_simplex.v"
-        `include "rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_rx_channel_init_sm_simplex.v"
-        `include "rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_rx_err_detect_simplex.v"
-        `include "rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_rx_global_logic_simplex.v"
-        `include "rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_rx_lane_init_sm_simplex.v"
-        `include "rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_rx_ll_datapath_simplex.v"
-        `include "rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_rx_ll_simplex.v"
-        `include "rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_rx_ll_user_k_datapath_simplex.v"
-        `include "rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_rx_startup_fsm.v"
-        `include "rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_sym_dec.v"
-
-        `include "rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/example_design/support/aurora_64b66b_1lane_clock_module.v"
-        `include "rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/example_design/support/aurora_64b66b_1lane_gt_common_wrapper.v"
-        `include "rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/example_design/support/aurora_64b66b_1lane_support.v"
-        `include "rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/example_design/support/aurora_64b66b_1lane_support_reset_logic.v"
-
-        `include "rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/example_design/gt/aurora_64b66b_1lane_multi_wrapper.v"
-        `include "rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/example_design/gt/aurora_64b66b_1lane_wrapper.v"
-        `include "rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/example_design/gt/aurora_64b66b_1lane_gtx.v"
-
-    `endif
+`ifdef RTL_SIM
+    `define SIM_ENCRYPTED
+`endif
+
+`include "rx_aurora/rx_aurora_64b66b_1lane/ip/exdes/aurora_64b66b_1lane_cdc_sync_exdes.v"
+
+`ifdef SIM_ENCRYPTED    // use the encrypted ip core for the GitLab CI
+    `include "rx_aurora/rx_aurora_64b66b_1lane/ip/sim/core.vp"
+`else   // otherwise we use the non-encrypted version
+    `include "rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane.v"
+    `include "rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane_core.v"
+
+    `include "rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_64b66b_descrambler.v"
+    `include "rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_block_sync_sm.v"
+    `include "rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_cbcc_gtx_6466.v"
+    `include "rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_cdc_sync.v"
+    `include "rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_common_logic_cbcc.v"
+    `include "rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_common_reset_cbcc.v"
+    `include "rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_ll_to_axi.v"
+    `include "rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_reset_logic.v"
+    `include "rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_rx_aurora_lane_simplex.v"
+    `include "rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_rx_channel_err_detect_simplex.v"
+    `include "rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_rx_channel_init_sm_simplex.v"
+    `include "rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_rx_err_detect_simplex.v"
+    `include "rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_rx_global_logic_simplex.v"
+    `include "rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_rx_lane_init_sm_simplex.v"
+    `include "rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_rx_ll_datapath_simplex.v"
+    `include "rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_rx_ll_simplex.v"
+    `include "rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_rx_ll_user_k_datapath_simplex.v"
+    `include "rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_rx_startup_fsm.v"
+    `include "rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_sym_dec.v"
+
+    `include "rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/example_design/support/aurora_64b66b_1lane_clock_module.v"
+    `include "rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/example_design/support/aurora_64b66b_1lane_gt_common_wrapper.v"
+    `include "rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/example_design/support/aurora_64b66b_1lane_support.v"
+    `include "rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/example_design/support/aurora_64b66b_1lane_support_reset_logic.v"
+
+    `include "rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/example_design/gt/aurora_64b66b_1lane_multi_wrapper.v"
+    `include "rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/example_design/gt/aurora_64b66b_1lane_wrapper.v"
+    `include "rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/example_design/gt/aurora_64b66b_1lane_gtx.v"
 `endif
 
 
diff --git a/firmware/src/rx_aurora/rx_aurora_64b66b_1lane/rx_aurora_64b66b_core.v b/firmware/src/rx_aurora/rx_aurora_64b66b_1lane/rx_aurora_64b66b_core.v
index 81c26d90e66909f0f4e00d994174b02ccea170ef..b3521f9a0ee9c0620840076fee126f5b46b1a96f 100755
--- a/firmware/src/rx_aurora/rx_aurora_64b66b_1lane/rx_aurora_64b66b_core.v
+++ b/firmware/src/rx_aurora/rx_aurora_64b66b_1lane/rx_aurora_64b66b_core.v
@@ -8,10 +8,7 @@
 `timescale 1ns/1ps
 `default_nettype none
 
-`ifndef SYNTHESIS
     `include "rx_aurora/rx_aurora_64b66b_1lane/ip/exdes/aurora_64b66b_1lane_exdes.v"
-`endif
-
 
 module rx_aurora_64b66b_core
 #(
diff --git a/firmware/vivado/ci_run.tcl b/firmware/vivado/ci_run.tcl
new file mode 100644
index 0000000000000000000000000000000000000000..3bb99a08c60ecf59c3cfae1ea27a0b876b18d0f4
--- /dev/null
+++ b/firmware/vivado/ci_run.tcl
@@ -0,0 +1,91 @@
+
+# ---------------------------------------------------------------
+# Copyright (c) SILAB ,  Institute of Physics, University of Bonn
+# ---------------------------------------------------------------
+#
+#   This script creates Vivado projects and bitfiles for the supported hardware platforms
+#
+#   Start vivado in tcl mode by typing:
+#       vivado -mode tcl -source run.tcl
+#
+
+
+set basil_dir [exec python -c "import basil, os; print(str(os.path.dirname(os.path.dirname(basil.__file__))))"]
+set include_dirs [list $basil_dir/basil/firmware/modules $basil_dir/basil/firmware/modules/utils]
+
+file mkdir output reports
+
+
+proc read_design_files {} {
+    read_verilog ../src/bdaq53.v
+    read_verilog ../src/bdaq53_core.v
+
+    read_edif ../SiTCP/SiTCP_XC7K_32K_BBT_V110.ngc
+    read_verilog ../SiTCP/TIMER.v
+    read_verilog ../SiTCP/SiTCP_XC7K_32K_BBT_V110.V
+    read_verilog ../SiTCP/WRAP_SiTCP_GMII_XC7K_32K.V
+
+
+    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/rx_aurora_64b66b.v
+    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/rx_aurora_64b66b_core.v
+
+    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/exdes/aurora_64b66b_1lane_exdes.v
+    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/exdes/aurora_64b66b_1lane_cdc_sync_exdes.v
+
+    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane.v
+    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane_core.v
+
+    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_64b66b_descrambler.v
+    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_block_sync_sm.v
+    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_cbcc_gtx_6466.v
+    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_cdc_sync.v
+    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_common_logic_cbcc.v
+    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_common_reset_cbcc.v
+    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_ll_to_axi.v
+    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_reset_logic.v
+    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_rx_aurora_lane_simplex.v
+    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_rx_channel_err_detect_simplex.v
+    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_rx_channel_init_sm_simplex.v
+    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_rx_err_detect_simplex.v
+    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_rx_global_logic_simplex.v
+    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_rx_lane_init_sm_simplex.v
+    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_rx_ll_datapath_simplex.v
+    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_rx_ll_simplex.v
+    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_rx_ll_user_k_datapath_simplex.v
+    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_rx_startup_fsm.v
+    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_sym_dec.v
+
+    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/example_design/support/aurora_64b66b_1lane_clock_module.v
+    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/example_design/support/aurora_64b66b_1lane_gt_common_wrapper.v
+    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/example_design/support/aurora_64b66b_1lane_support.v
+    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/example_design/support/aurora_64b66b_1lane_support_reset_logic.v
+
+    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/example_design/gt/aurora_64b66b_1lane_multi_wrapper.v
+    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/example_design/gt/aurora_64b66b_1lane_wrapper.v
+    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/example_design/gt/aurora_64b66b_1lane_gtx.v
+}
+
+
+proc run_bit { part board connector xdc_file size option} {
+    create_project -force -part $part $board$option$connector designs
+
+    read_design_files
+    read_xdc $xdc_file
+
+    #read_ip ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane.xci
+    global include_dirs
+
+    synth_design -top bdaq53 -include_dirs $include_dirs -verilog_define "$board=1" -verilog_define "$connector=1" -verilog_define "SYNTHESIS=1" -verilog_define "$option=1"
+    opt_design
+    place_design
+    phys_opt_design
+    route_design
+    report_utilization
+    report_timing -file "reports/report_timing.$board$option$connector.log"
+    write_bitstream -force -file output/$board$option$connector
+    write_cfgmem -format mcs -size $size -interface SPIx4 -loadbit "up 0x0 output/$board$option$connector.bit" -force -file output/$board$option$connector
+    close_project
+
+    exec tar -C ./output -cvzf output/$board$option$connector.tar.gz $board$option$connector.bit $board$option$connector.mcs
+}
+
diff --git a/firmware/vivado/run.tcl b/firmware/vivado/run.tcl
index c83e771d2c257f8c93c0a574baf89052be72d46e..f201e6372be718e2a96b6adc70e4d6dfca8b41ee 100644
--- a/firmware/vivado/run.tcl
+++ b/firmware/vivado/run.tcl
@@ -3,15 +3,20 @@
 # Copyright (c) SILAB ,  Institute of Physics, University of Bonn
 # ---------------------------------------------------------------
 #
-#   This script creates Vivado projects and bitfiles for the supported hardware platforms
+#   This script creates Vivado projects and bitfiles for the supported hardware platforms.
 #
-#   Start vivado in tcl mode by typing:
+#   Start vivado in tcl mode by executing:
 #       vivado -mode tcl -source run.tcl
 #
+#   NOTE: This will build firmware versions for every supported hardware. See the section "Create projects and bitfiles" below.
+#   Alternatively, a list of 6 arguments can be used to build only the specified firmware.
+#   The arguments have to be in the correct order. Just copy&paste from the "Create projects and bitfiles" section and remove all but one space in between the args.
+#       vivado -mode tcl -source run.tcl -tclargs xc7k160tffg676-2 BDAQ53 "" ../src/bdaq53.xdc 64 _RX640
+#
 
 
-set basil_dir [exec python -c "import basil, os; print(str(os.path.dirname(basil.__file__)))"]
-set include_dirs [list $basil_dir/firmware/modules $basil_dir/firmware/modules/utils]
+set basil_dir [exec python -c "import basil, os; print(str(os.path.dirname(os.path.dirname(basil.__file__))))"]
+set include_dirs [list $basil_dir/basil/firmware/modules $basil_dir/basil/firmware/modules/utils]
 
 file mkdir output reports
 
@@ -24,45 +29,6 @@ proc read_design_files {} {
     read_verilog ../SiTCP/TIMER.v
     read_verilog ../SiTCP/SiTCP_XC7K_32K_BBT_V110.V
     read_verilog ../SiTCP/WRAP_SiTCP_GMII_XC7K_32K.V
-
-
-    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/rx_aurora_64b66b.v
-    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/rx_aurora_64b66b_core.v
-
-    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/exdes/aurora_64b66b_1lane_exdes.v
-    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/exdes/aurora_64b66b_1lane_cdc_sync_exdes.v
-
-    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane.v
-    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane_core.v
-
-    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_64b66b_descrambler.v
-    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_block_sync_sm.v
-    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_cbcc_gtx_6466.v
-    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_cdc_sync.v
-    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_common_logic_cbcc.v
-    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_common_reset_cbcc.v
-    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_ll_to_axi.v
-    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_reset_logic.v
-    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_rx_aurora_lane_simplex.v
-    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_rx_channel_err_detect_simplex.v
-    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_rx_channel_init_sm_simplex.v
-    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_rx_err_detect_simplex.v
-    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_rx_global_logic_simplex.v
-    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_rx_lane_init_sm_simplex.v
-    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_rx_ll_datapath_simplex.v
-    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_rx_ll_simplex.v
-    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_rx_ll_user_k_datapath_simplex.v
-    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_rx_startup_fsm.v
-    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/src/aurora_64b66b_1lane_sym_dec.v
-
-    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/example_design/support/aurora_64b66b_1lane_clock_module.v
-    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/example_design/support/aurora_64b66b_1lane_gt_common_wrapper.v
-    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/example_design/support/aurora_64b66b_1lane_support.v
-    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/example_design/support/aurora_64b66b_1lane_support_reset_logic.v
-
-    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/example_design/gt/aurora_64b66b_1lane_multi_wrapper.v
-    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/example_design/gt/aurora_64b66b_1lane_wrapper.v
-    read_verilog ../src/rx_aurora/rx_aurora_64b66b_1lane/ip/aurora_64b66b_1lane/aurora_64b66b_1lane/example_design/gt/aurora_64b66b_1lane_gtx.v
 }
 
 
@@ -96,18 +62,29 @@ proc run_bit { part board connector xdc_file size option} {
 # Create projects and bitfiles
 #
 
-# Bitfiles for the 1.28 Gb/s Aurora ip core configuration
-#       FPGA type           board name	connector  	constraints file     flash size  option
-run_bit xc7k160tffg676-2    BDAQ53      ""          ../src/bdaq53.xdc       64        ""
-run_bit xc7k160tfbg676-1    USBPIX3     ""          ../src/usbpix3.xdc      64        ""
-run_bit xc7k325tffg900-2    KC705       _SMA        ../src/kc705_gmii.xdc   16        ""
-run_bit xc7k325tffg900-2    KC705       _FMC_LPC    ../src/kc705_gmii.xdc   16        ""
+if {$argc == 0} {
+# By default, all firmware versions are generated. You can comment the ones you don't need.
 
 # Bitfiles for the 640 Mb/s Aurora ip core configuration
-#       FPGA type           board name  connector	constraints file     flash size  option
+#       FPGA type           board name  connector   constraints file     flash size  option
 run_bit xc7k160tffg676-2    BDAQ53      ""          ../src/bdaq53.xdc       64      _RX640
 run_bit xc7k160tfbg676-1    USBPIX3     ""          ../src/usbpix3.xdc      64      _RX640
 run_bit xc7k325tffg900-2    KC705       _SMA        ../src/kc705_gmii.xdc   16      _RX640
 run_bit xc7k325tffg900-2    KC705       _FMC_LPC    ../src/kc705_gmii.xdc   16      _RX640
 
+# Bitfiles for the 1.28 Gb/s Aurora ip core configuration
+#       FPGA type           board name	connector  	constraints file     flash size  option
+run_bit xc7k160tffg676-2    BDAQ53      ""          ../src/bdaq53.xdc       64        ""
+run_bit xc7k160tfbg676-1    USBPIX3     ""          ../src/usbpix3.xdc      64        ""
+run_bit xc7k325tffg900-2    KC705       _SMA        ../src/kc705_gmii.xdc   16        ""
+run_bit xc7k325tffg900-2    KC705       _FMC_LPC    ../src/kc705_gmii.xdc   16        ""
+
+# In order to build only one specific firmware version, the tun.tcl can be executed with arguments
+} else {
+    if {$argc == 6} {
+        run_bit {*}$argv
+    } else {
+        puts "ERROR: Invalid args"
+    }
+}
 exit