diff --git a/doc/args_class_diagram.pdf b/doc/args_class_diagram.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..4d2515387c209b2793e91fc11286ce33b965757f
Binary files /dev/null and b/doc/args_class_diagram.pdf differ
diff --git a/gen_bus.py b/gen_bus.py
index 1e0b8143b708bc13a64c664dbff4e1083cadd48a..1a01c0e9aa5ea3dbe88459fe8afa994cfef9006a 100755
--- a/gen_bus.py
+++ b/gen_bus.py
@@ -12,12 +12,13 @@
 
 import os
 import logging
-import common as cm
+#import common as cm
 import collections
 from py_args_lib import *
 from fpga import FPGA
 import numpy as np
 from gen_slave import tab_aligned
+
 logger = logging.getLogger('main.gen_bus')
 
 
@@ -32,7 +33,7 @@ class Bus(object):
     def __init__(self, fpga):
         self.fpga = fpga
         self.root_dir   = os.path.expandvars('$ARGS_GEAR')
-        self.out_dir    = os.path.expandvars('$ARGS_BUILD_DIR/{}/hdl'.format(self.fpga.fpga_name))
+        self.out_dir    = check_outdir(self.fpga.board_name, self.fpga.fpga_name, 'hdl')
         self.tmpl_mstr_port_full = os.path.join(self.root_dir, 'templates/template_bus_master_axi_full.vho')
         self.tmpl_mstr_port_lite = os.path.join(self.root_dir, 'templates/template_bus_master_axi_lite.vho')
         self.tmpl_mstr_port_cast = os.path.join(self.root_dir, 'templates/template_bus_master_port_casting.vho')
@@ -92,11 +93,13 @@ class Bus(object):
                     i = -1
                     # for i, slave_attr in enumerate(self.fpga.address_map.values()):
                     for slave_attr in self.fpga.address_map.values():
+                        logger.debug('slave_attr=%s', str(slave_attr))
                         if slave_attr['port_index'] == last_port and slave_attr['type'] == last_prot:
                             continue  # skip port indexes already dealt with
                         i = i+1
                         last_port = slave_attr['port_index']
                         last_prot = slave_attr['type']
+                        logger.debug('indexes=%s', str(self.indexes))
                         if np.mod(i, 15) == 0 and self.indexes[i][1] == 0 and i != 0:
                             lines.append(get_cmd(input[line_num+4]))  # daisy chain interconnects
                             lines.append(get_cmd(input[line_num+5]).format(self.indexes[i][0]-1, self.indexes[i][0]))
@@ -122,9 +125,9 @@ class Bus(object):
                         last_prot = slave_attr['type']
                         if isinstance(slave_attr['slave'], Register):
                             if not getattr(slave_attr['slave'], 'isIP', False):
-                                span = cm.ceil_pow2(max(slave_attr['peripheral'].reg_len, 4096))
+                                span = ceil_pow2(max(slave_attr['peripheral'].reg_len, 4096))
                             else :
-                                span = cm.ceil_pow2(max(slave_attr['slave'].address_length(), 4096))
+                                span = ceil_pow2(max(slave_attr['slave'].address_length(), 4096))
                         else :
                             span = slave_attr['span']
                         _line = get_cmd(line).replace('<range>', '4')  # 4k is minimum settable size for vivado
@@ -153,9 +156,9 @@ class Bus(object):
                         last_prot = slave_attr['type']
                         if isinstance(slave_attr['slave'], Register):
                             if not getattr(slave_attr['slave'], 'isIP', False):
-                                span = cm.ceil_pow2(max(slave_attr['peripheral'].reg_len, 4096))
+                                span = ceil_pow2(max(slave_attr['peripheral'].reg_len, 4096))
                             else :
-                                span = cm.ceil_pow2(max(slave_attr['slave'].address_length(), 4096))
+                                span = ceil_pow2(max(slave_attr['slave'].address_length(), 4096))
                         else :
                             span = slave_attr['span']
                         lines.append('# interconnect[{}]  <{}>   base: 0x{:08x} span: 0x{:06x}\n'.format(
@@ -298,6 +301,8 @@ class Bus(object):
         """
         Calculate interconnects and local slave port numbers for all slaves
         """
+        logger.debug("enter calc_indexes()")
+        logger.debug("nof_slaves=%d", self.nof_slaves)
         index_list = []
         for i in range(self.nof_slaves):
             if np.floor(i/15) < self.nof_interconnects:
@@ -312,5 +317,6 @@ class Bus(object):
                          i, interconnect_num, local_slave_num, self.nof_interconnects))
 
             index_list.append((interconnect_num, local_slave_num))
+        
         return index_list
 
diff --git a/gen_fpgamap_py.py b/gen_fpgamap_py.py
index 218fa6c83a0fb5cd0deefc220fedddf8c85cdc18..bcf84ac016b804e161106b388560629bbc625bdd 100755
--- a/gen_fpgamap_py.py
+++ b/gen_fpgamap_py.py
@@ -38,7 +38,7 @@ import pprint
 # import code
 
 
-def genPython(fpga, fpgaName, readable):
+def genPython(fpga, fpgaName, addr_size=4, readable=False):
     slavePorts = {}
     print("Including slave ports for {}:".format(fpgaName))
     for slavePortName, slavePortInfo in fpga.address_map.items():
@@ -46,11 +46,11 @@ def genPython(fpga, fpgaName, readable):
             continue
         peripheral = slavePortInfo['peripheral']
         slave = slavePortInfo['slave']
-        base = int(slavePortInfo['base']/addr_size)  # Convert from AXI byte address to register address
+        base = int(slavePortInfo['base'] / addr_size)  # Convert from AXI byte address to register address
         if peripheral.name() not in slavePorts:
-            slavePorts[peripheral.name()] = {'slaves': {}, 'start': base, 'span': int(slavePortInfo['span']/4), 'count': peripheral.number_of_peripherals()}
+            slavePorts[peripheral.name()] = {'slaves': {}, 'start': base, 'span': int(slavePortInfo['span'] / addr_size), 'count': peripheral.number_of_peripherals()}
         else:
-            slavePorts[peripheral.name()]['span'] += int(slavePortInfo['span']/addr_size)
+            slavePorts[peripheral.name()]['span'] += int(slavePortInfo['span'] / addr_size)
         slaves = slavePorts[peripheral.name()]['slaves']
         slaveOffset = base - slavePorts[peripheral.name()]['start']
         if isinstance(slave, RAM):
@@ -197,4 +197,4 @@ if __name__ == '__main__':
     fpga_lib = FPGALibrary(root_dir=libRootDir, use_avalon_base_addr=useAvalon)
     fpga = fpga_lib.get_fpga(fpgaName)
 
-    genPython(fpga, fpgaName, readable)
+    genPython(fpga, fpgaName, addr_size, readable)
diff --git a/gen_hdl.py b/gen_hdl.py
new file mode 100755
index 0000000000000000000000000000000000000000..bc063c9a0b7afdfa644e8d186062133cdb8339ad
--- /dev/null
+++ b/gen_hdl.py
@@ -0,0 +1,193 @@
+#! /usr/bin/env python3
+
+###############################################################################
+#
+# Copyright (C) 2016
+# ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
+# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+# Author  Date
+#   PD    apr 2020  Original
+#
+###############################################################################
+
+import os
+import sys
+import traceback
+import argparse
+import subprocess
+
+from args_logger import MyLogger
+
+from py_args_lib import *
+
+import gen_slave
+import gen_bus
+import gen_doc
+import gen_fpgamap_py
+
+
+def main():
+
+    # root directory to find hdl.cfg and yaml files
+    lib_root_dir = os.path.expandvars('$ARGS_WORK')
+
+
+    # Find and parse all *.fpga.yaml YAML files under root directory for RADIOHDL
+    # For each FPGA file it extracts a list of the peripherals that are used by that FPGA
+    # Here we select the FPGA YAML that matches up with the supplied fpga command line argument
+    addr_size = 1 if use_avalon is True else 4
+
+    fpga_lib = FPGALibrary(root_dir=lib_root_dir, use_avalon_base_addr=use_avalon)
+    fpga = fpga_lib.get_fpga(fpga_name)
+    
+    # create the summary file in the output directory
+    out_dir = check_outdir(fpga.board_name.replace('uniboard', 'unb'), fpga_name, 'hdl')
+
+    hdl_gen = HDLGen(out_dir, fpga, addr_size)
+
+
+class HDLGen(object):
+    """
+    """
+    def __init__(self, outdir, fpga, addr_size):
+        self.outdir       = outdir
+        self.fpga         = fpga
+        self.addr_size    = addr_size
+        self.hdl_lib_name = self.fpga.hdl_library_name
+        self.fpga_name    = self.fpga.fpga_name
+        self.board_name   = self.fpga.board_name
+
+        self.args_generated = []
+
+        self.handle_args_lib_references(use_name=self.fpga_name, is_fpga=True)
+
+        # for lib_name in self.fpga.peripherals:
+        #     # library of peripherals that should have names unique from self.periph_lib_names
+        #     # generate files and append to lib dict's synth_files and vivado_tcl_files
+        #     self.handle_args_lib_references(use_name=lib_name)
+
+    def handle_args_lib_references(self, use_name, is_fpga=False):
+        """
+        """
+        logger.debug('handle_args_lib_reference use_name = %s', str(use_name))
+        use_name_keep = use_name
+        use_name_split = use_name.split('/')
+        lib_name = use_name_split[0]
+        periph_name = None if len(use_name_split) == 1 else use_name_split[1]
+        args_lib_name = None if periph_name is None else '_'.join(use_name_split)  # if peripheral is specified in fpga.yaml
+
+        self.gen_args_lib_dict(lib_name, periph_name, args_lib_name, is_fpga)
+
+        return
+
+    def gen_args_lib_dict(self, args_lib_name, periph_select=None, lib_name_custom=None, is_fpga=False):  # to support peripheral.py and fpga.py
+
+        final_name = args_lib_name if lib_name_custom is None else lib_name_custom
+        if (final_name in self.args_generated or lib_name_custom is not None) and is_fpga is False :
+            logger.warning("ARGS has already generated firmware and documentation for the library %s, will not repeat.",
+                           args_lib_name)  # have to tolerate when part of fpga
+            return None
+        logger.debug("gen_args_lib_dict hdl_lib_dict '%s' args_lib_name '%s' lib_name_custom '%s'",
+                     final_name, args_lib_name, lib_name_custom)
+        
+        peripherals = self.fpga.peripherals if is_fpga else self.periph_libs[args_lib_name]['peripherals']
+        
+        if is_fpga:
+            fpga_bus = gen_bus.Bus(self.fpga)
+            gen_fpgamap_py.genPython(fpga=self.fpga, fpgaName=self.fpga_name, addr_size=self.addr_size, readable=True)
+            fpga_bus.gen_firmware()
+            out_dir = check_outdir(self.board_name, self.fpga_name, 'hdl')
+        else:    
+            out_dir = check_outdir(self.board_name, self.fpga_name, 'hdl')
+
+        for component_name, peripheral in peripherals.items():
+            # if periph_select != 'None' and periph['peripheral_name'] != periph_select:
+            # logger.warning("peripheral name: {}".format(component_name))
+            if periph_select is not None and component_name != periph_select:
+                continue
+            if peripheral.evaluated is False:
+                peripheral.eval_peripheral()
+            periph_slave = gen_slave.Slave(peripheral, self.hdl_lib_name, args_lib_name if is_fpga else None)
+            periph_slave.generate_regs(out_dir)
+            for key in peripheral.rams:
+                periph_slave.generate_mem(out_dir, peripheral.rams[key], 'ram')
+            for key in peripheral.fifos:
+                periph_slave.generate_mem(out_dir, peripheral.fifos[key], 'fifo')
+
+        self.args_gen_doc(final_name, is_fpga)
+        if periph_select is None:
+            self.args_generated.append(final_name)
+
+        return
+
+    def args_gen_doc(self, lib_name, is_fpga):  # , output_files):
+        #devnull = open(os.devnull, 'w')
+        try:
+            subprocess.call("pdflatex -version", stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, shell=True)
+        except FileNotFoundError:
+            logger.warning("No latex distribution detected, skipping ARGS documentation generation")
+            return
+
+        logger.debug('Generating PDF documentation for library %s...', lib_name)
+        if is_fpga:
+            doc = gen_doc.FPGADocumentation(lib_name, self.fpga)
+            doc.fill()
+            doc.make_pdf()
+            del doc
+        else:
+            doc = gen_doc.PeripheralDocumentation(lib_name, self.periph_libs[lib_name])
+            doc.fill()
+            doc.make_pdf()
+            del doc
+        return
+
+
+if __name__ == '__main__':
+
+    parser = argparse.ArgumentParser(description='ARGS tool script to generate hdl code')
+    parser.add_argument('-f', '--fpga', required=True, help='ARGS fpga_name, example="unb2b_minimal"')
+    parser.add_argument('-a', '--avalon', action='store_true', default=False, help='use qsys/sopc config file for base addresses')
+    parser.add_argument('-v', '--verbosity', default='INFO', help='stdout log level, can be [ERROR | WARNING | INFO | DEBUG]')
+    args = parser.parse_args()
+
+    fpga_name = args.fpga
+    use_avalon = args.avalon
+    
+    if use_avalon:
+        sys.exit("-a, --avalon option is not working in this AXI version of software, it is for future development")
+
+    # setup first log system before importing other user libraries
+    program_name = __file__.split('/')[-1].split('.')[0]
+    out_dir = os.path.join(os.getenv('ARGS_GEAR'), 'log')
+    my_logger = MyLogger(log_path=out_dir, file_name=program_name)
+    my_logger.set_file_log_level('DEBUG')
+    my_logger.set_stdout_log_level(args.verbosity)
+
+    logger = my_logger.get_logger()
+    logger.debug("Used arguments: {}".format(args))
+
+    try:
+        main()
+    except (ARGSNameError, ARGSModeError, ARGSYamlError):
+        handle_args_error(sys.exc_info()[0])
+    except:
+        logger.error('Program fault, reporting and cleanup')
+        logger.error('Caught %s', str(sys.exc_info()[0]))
+        logger.error(str(sys.exc_info()[1]))
+        logger.error('TRACEBACK:\n%s', traceback.format_exc())
+        logger.error('Aborting NOW')
+        sys.exit("ERROR")
diff --git a/gen_slave.py b/gen_slave.py
index 8eb32e786bc74f83952f3e50e9d017292c446cd3..f0fedc0bd46e9b9b5a4663ad19cf30fe593c0883 100755
--- a/gen_slave.py
+++ b/gen_slave.py
@@ -7,7 +7,6 @@ import subprocess
 import traceback
 import shutil
 import numpy as np
-from common import ceil_log2, ceil_pow2
 from constants import *
 from peripheral_lib import *
 import peripheral
@@ -26,7 +25,7 @@ logger = logging.getLogger('main.gen_slave')
 
 
 def word_wise(byte_address):
-    return int(byte_address/WIDTH_IN_BYTES)
+    return max(1, int(byte_address / WIDTH_IN_BYTES))
 
 
 def tab_aligned(input_strings):
@@ -74,19 +73,21 @@ def vector_len(width):
 class Slave(object):
     """Generate VHDL and IP source for memory mapped slaves"""
 
-    def __init__(self, peripheral, fpga_name=None):
-        self.rootDir = os.path.expandvars('$RADIOHDL/tools/args')  # ('$RADIOHDL/tools/prestudy/YAML')
+    def __init__(self, peripheral, hdl_lib_name, fpga_name=None):
+        self.tmpl_dir = os.path.join(os.getenv('ARGS_GEAR'), 'templates')
+        self.out_dir = None
         self.nof_dat = 0
         self.replaceDict = {}
         self.periph_name = peripheral.name()
-        self.periph_lib = peripheral.lib
+        self.periph_lib = hdl_lib_name
         self.slaves = peripheral.slaves
-        self.prefix = ((peripheral.lib + '_') if peripheral.lib != peripheral.name() else '') + peripheral.name()
+        self.prefix = ((hdl_lib_name + '_') if hdl_lib_name != peripheral.name() else '') + peripheral.name()
         self.output_files = []
         self.fpga_name = fpga_name
 
-    def generate_mem(self, settings, slave_type):
+    def generate_mem(self, outdir, settings, slave_type):
         """ Generate a VHDL instantiation file and a TCL file to create and customise the IP """
+        self.out_dir = outdir
         lines = []
         # fix memory to fit Xilinx IP width/depth combinations
         # self.xilinxConstraints()
@@ -94,10 +95,9 @@ class Slave(object):
         self.gen_file(settings, slave_type, 'vhd')
         self.gen_file(settings, slave_type, 'tcl')
 
-    def generate_regs(self, peripheral):
+    def generate_regs(self, outdir):
         """ Generate a set of entity, pkg and instantiation files for all reg slaves """
-        self.periph_name = peripheral.name()
-        self.periph_lib = peripheral.lib
+        self.out_dir = outdir
         self.nof_dat = 0
         for slave in self.slaves:
             if not isinstance(slave, Register) or (isinstance(slave, Register) and getattr(slave, 'isIP', False)):
@@ -113,23 +113,13 @@ class Slave(object):
             self.gen_file(None, 'reg', 'vho')
 
     def write_file(self, lines, file_name):
-        out_dir = os.path.expandvars('$HDL_BUILD_DIR/ARGS')
-        if self.fpga_name is not None:
-            out_dir = os.path.join(out_dir, self.fpga_name)
-        try:
-            os.stat(out_dir)
-        except:
-            os.mkdir(out_dir)
-        out_dir = os.path.join(out_dir, self.periph_lib)
-        try:
-            os.stat(out_dir)
-        except:
-            os.mkdir(out_dir)
-        out_dir = os.path.join(out_dir, self.periph_name)
+        out_dir = self.out_dir
+        out_dir = os.path.join(out_dir, self.periph_lib, self.periph_name)
         try:
-            os.stat(out_dir)
-        except:
-            os.mkdir(out_dir)
+            os.stat(out_dir)  # Check that the output directory exists
+        except FileNotFoundError:
+            os.makedirs(out_dir)  # if not make it
+
         file_name = os.path.join(out_dir, file_name)
         with open(file_name, 'w') as outFile:
             for line in lines:
@@ -141,9 +131,7 @@ class Slave(object):
         return self.output_files
 
     def gen_pkg(self):
-        tmplFile = os.path.join(self.rootDir, "templates/template_reg_pkg.vhd")
-        # outDir = os.path.join(self.rootDir, 'outputs')
-        # pkgFile = os.path.join(outDir, self.periph_lib + '_' + self.periph_name + "_reg_pkg.vhd")
+        tmplFile = os.path.join(self.tmpl_dir, "template_reg_pkg.vhd")
         file_name = self.prefix + "_reg_pkg.vhd"
         lines = []
         # fields_dict = regGroup.fields
@@ -189,7 +177,7 @@ class Slave(object):
 
     def gen_vho(self, slaveSettings, slave_type):
         lines = []
-        tmplFile = os.path.join(self.rootDir, "templates/template_reg_axi4.vho")
+        tmplFile = os.path.join(self.tmpl_dir, "template_reg_axi4.vho")
         # fields_dict = slaveSettings.fields
         with open(tmplFile, 'r') as infile:
             for line in infile:
@@ -222,7 +210,7 @@ class Slave(object):
     def gen_vhdl(self, slaveSettings, slave_type):
         lines = []
         slave_type = slave_type.lower()
-        tmplFile = os.path.join(self.rootDir, "templates/template_" + slave_type + "_axi4.vhd")
+        tmplFile = os.path.join(self.tmpl_dir, "template_{}_axi4.vhd".format(slave_type))
         removePort = {}
         replace_dicts = {}
         if slave_type == 'reg':
@@ -319,10 +307,10 @@ class Slave(object):
                             lines.extend(self.instantiate_rams())
                             sublines = []
                             if slave.get_kv("dual_clock"):
-                                tmpl_file = os.path.join(self.rootDir, 'templates/template_common_reg_r_w_dc.vho')
+                                tmpl_file = os.path.join(self.tmpl_dir, 'template_common_reg_r_w_dc.vho')
                                 # Need to add support for peripherals with different kinds of dual clock slaves
                             else:
-                                tmpl_file = os.path.join(self.rootDir, 'templates/template_common_reg_r_w.vho')
+                                tmpl_file = os.path.join(self.tmpl_dir, 'template_common_reg_r_w.vho')
 
                             with open(tmpl_file, 'r') as inst_file:
                                 sublines = [(subline.replace('<reg_name>', regGroup.name())) for subline in inst_file]
@@ -351,7 +339,7 @@ class Slave(object):
 
     def gen_tcl(self, settings, slave_type):
         lines = []
-        tmplFile = os.path.join(self.rootDir, "templates/template_" + slave_type + "_axi4.tcl")
+        tmplFile = os.path.join(self.tmpl_dir, "template_{}_axi4.tcl".format(slave_type))
         # replaceDict = {'<entity>': settings.name, '<FW>' : settings.access_mode() == 'FW', '<init_sl>' : settings.default(), '<bDefault>':settings.default() is not None,
                     # '<dat_w>' : settings.width(), '<FR>' : settings.access_mode() == 'FR', '<nof_dat>' : settings.address_length(), '<FTHRESHOLD>' : settings.address_length() - 5, '<ETHRESHOLD>' : 2 }
         default = settings.reset_value() if settings.reset_value() is not None else 0
@@ -412,7 +400,7 @@ class Slave(object):
 
     def set_clr_mask(self, regGroup):
         field_list = regGroup.fields
-        temp_vector = [0]*word_wise(regGroup.address_length())*self.dat_w
+        temp_vector = [0]*(word_wise(regGroup.address_length())*self.dat_w)
         bMask = 0
         padding = ''
         for field in field_list:
@@ -420,6 +408,7 @@ class Slave(object):
                 bMask = 1
                 lowestBit = None if word_wise(field.address_offset())*self.dat_w+field.bit_offset() == 0 else word_wise(field.address_offset())*self.dat_w+field.bit_offset() - 1
                 temp_vector[word_wise(field.address_offset())*self.dat_w+field.bit_offset() + field.width()-1 :lowestBit:-1] = [1]*field.width()  # list(format(field.default(), '0' + str(field.width()) + 'b'))
+        
         temp_string = ''.join(map(str, temp_vector[::-1]))
         hexValue = hex(int(temp_string, 2))[2:]
         if len(hexValue) < int(word_wise(regGroup.address_length())*self.dat_w/4):
@@ -687,7 +676,7 @@ class Slave(object):
             sublines.append(('\t<>_adr <= wr_adr(c_mm_<>_ram.adr_w-1 downto 0) WHEN <>_wr_en ' + ('= \'1\'' if self.regGroup.number_of_slaves() == 1 else '/= (<>_wr_en\'range => \'0\')')+' ELSE\n\t\t\t\trd_adr(c_mm_<>_ram.adr_w-1 downto 0);\n\n ').replace('<>', self.regGroup.name() + '_' + ram.name()))
             if self.regGroup.number_of_slaves() > 1:
                 sublines.append(('\t<>_' + ram.name() + '_gen: FOR i in 0 to c_mm_<>_reg.nof_slaves-1 GENERATE\n').replace('<>', self.regGroup.name()))
-            with open(os.path.join(self.rootDir, 'templates/template_common_ram_rw_rw.vho'), 'r') as inst_file:
+            with open(os.path.join(self.tmpl_dir, 'template_common_ram_rw_rw.vho'), 'r') as inst_file:
                 for subline in inst_file:
                     for tag, replace_string in {'<field_name>' : self.regGroup.name() + '_' + ram.name(), '<FIELD_NAME>': (self.regGroup.name() + '_' + ram.name()).upper(), '<reg_name>': self.regGroup.name(), '(i)' : '' if self.regGroup.number_of_slaves() == 1 else '(i)', '+ i' : '' if self.regGroup.number_of_slaves() == 1 else '+ i'}.items():
                         subline = subline.replace(tag, replace_string)
@@ -696,7 +685,7 @@ class Slave(object):
                     sublines.append(subline)
             if self.regGroup.number_of_slaves() > 1:
                 sublines.append('\tEND GENERATE;\n\n')
-            with open(os.path.join(self.rootDir, 'templates/template_common_pipeline.vho'), 'r') as inst_file:
+            with open(os.path.join(self.tmpl_dir, 'template_common_pipeline.vho'), 'r') as inst_file:
                 # sublines.extend(subline.replace('<field_name>', regGroup.name() + '_' + ram.name()) for subline in inst_file)
                 for subline in inst_file:
                     if '<nof_slaves>' in subline:
@@ -711,7 +700,7 @@ class Slave(object):
 
     def c_mm_reg(self, replace_dicts):
         lines = []
-        tmpl_file = os.path.join(self.rootDir, 'templates/template_c_mm_reg.vho')
+        tmpl_file = os.path.join(self.tmpl_dir, 'template_c_mm_reg.vho')
         for ram in self.regGroup.rams:
             sublines = []
             with open(tmpl_file, 'r') as inst_file:
diff --git a/py_args_lib/fpga.py b/py_args_lib/fpga.py
index 16247afbbdffba6179bb6aca00e306a54ae07eeb..3637587fd0cf12975008f725d5ac7ff9070e0eb6 100644
--- a/py_args_lib/fpga.py
+++ b/py_args_lib/fpga.py
@@ -52,7 +52,8 @@ class FPGA(object):
 
         self.use_avalon_base_addr = False if use_avalon_base_addr is None else use_avalon_base_addr
 
-        self.fpga_name = ""
+        self.hdl_library_name = ""
+        self.fpga_name        = ""
         self.fpga_description = ""
         self.parameters = {}
         # self.peripherals = {}
@@ -97,6 +98,7 @@ class FPGA(object):
         logger.debug("Creating FPGA")
         logger.debug("Instantiating the peripherals from the peripheral Library")
         # self.fpga_name = self.fpga_config['hdl_library_name']
+        self.hdl_library_name = self.fpga_config['hdl_library_name']
         self.fpga_name = self.fpga_config['fpga_name']
 
         if "fpga_description" in self.fpga_config:
@@ -513,12 +515,10 @@ class FPGALibrary(object):
         if root_dir is not None:
             for root, dirs, files in os.walk(self.root_dir, topdown=True):
                 if 'tools' in root:
-                    # skip tools dir
-                    continue
+                    continue  # skip tools dir
                 for name in files:
                     if not name.endswith(self.file_extension):
-                        # skip, no matching file extension
-                        continue
+                        continue  # skip, no matching file extension
                     try :
                         # try to read yaml file
                         library_config = yaml.load(open(os.path.join(root, name), 'r'))
@@ -530,7 +530,7 @@ class FPGALibrary(object):
                         #logger.error('ERROR:\n' + sys.exc_info()[1])
                         raise ARGSYamlError
                         continue
-                        #sys.exit()
+                        
 
                     if not isinstance(library_config, dict):
                         logger.warning('File %s is not readable as a dictionary, it will not be'
diff --git a/py_args_lib/peripheral.py b/py_args_lib/peripheral.py
index cc411fa9908cd28107d5fc9e9d38eb9f34645f54..e99ea51eae029e496d9bfd533dc8adc501f0c360 100644
--- a/py_args_lib/peripheral.py
+++ b/py_args_lib/peripheral.py
@@ -57,7 +57,7 @@ class Peripheral(BaseObject):
     The Peripheral evaluates the nof_inst and parameters to set the
     dimensions of the MM slaves.
     """
-    def __init__(self, hdl_library_name, library_config):
+    def __init__(self, library_config):
         super().__init__()
         self._config         = {}  # read config from file
         self._parameters     = {}  # all used parameters
@@ -66,7 +66,6 @@ class Peripheral(BaseObject):
         self.fifos           = {}  # all used fifos
         self.slaves          = []
         self._component_name = library_config['peripheral_name']
-        self.hdl_library_name = hdl_library_name
         self.name(self._component_name)
         self._valid_keys = ['number_of_peripherals']
         self._args.update({'number_of_peripherals' : DEFAULT_NUMBER_OF_PERIPHERALS})
@@ -190,7 +189,7 @@ class Peripheral(BaseObject):
             slave_ports = deepcopy(self._config['slave_ports'])
 
             if not isinstance(slave_ports, list):
-                logger.error("slave_ports not a list in %s.peripheral.yaml", self.hdl_library_name)
+                logger.error("slave_ports not a list in *.peripheral.yaml")
                 sys.exit()
 
             for slave_nr, slave_info in enumerate(slave_ports):
@@ -198,12 +197,11 @@ class Peripheral(BaseObject):
                 slave_name = slave_info['slave_name']
 
                 if slave_name is None:
-                    logger.error("Peripheral '%s': 'slave_name' key missing value in %s.peripheral.yaml", self.name(), self.hdl_library_name)
+                    logger.error("Peripheral '%s': 'slave_name' key missing value in *.peripheral.yaml", self.name())
                     sys.exit()
 
                 i = 0
-                _slave_type = slave_info.get('slave_type', '').upper()
-                if _slave_type in VALID_SLAVE_TYPES:
+                if slave_info.get('slave_type','').upper() in VALID_SLAVE_TYPES:
                     number_of_slaves = slave_info.get('number_of_slaves', DEFAULT_NUMBER_OF_SLAVES)
 
                     fields = []
@@ -232,8 +230,7 @@ class Peripheral(BaseObject):
                                     defaults = field_info['field_defaults']
                                     if any([key.lower() not in VALID_DEFAULT_KEYS for key in defaults.keys()]) :
                                         defaults = {}
-                                        logger.error("%s.peripheral.yaml: Invalid key set in defaults for field group %s. Valid keys are %s",
-                                                     self.hdl_library_name, group_label, str(VALID_DEFAULT_KEYS))
+                                        logger.error("{}.peripheral.yaml: Invalid key set in defaults for field group {}. Valid keys are {}".format(self.lib, group_label, VALID_DEFAULT_KEYS))
                                         sys.exit()
                                     continue
 
@@ -244,38 +241,41 @@ class Peripheral(BaseObject):
                                     # field = Field(field_name, _slave_type)
                                     field = Field(field_name, field_info)
                                 except ARGSNameError:
-                                    logger.error("Invalid name '%s' for field in %s.peripheral.yaml", field_name, self.hdl_library_name)
+                                    logger.error("Invalid name '%s' for field in *.peripheral.yaml", field_name)
                                     sys.exit()
                                 if group_label is not None :
                                     field.group_name(group_label)
+                                if field_name == "args_map_build":
+                                    logger.info("args_map_build = {}".format(Peripheral.__timestamp))
+                                    field_info['reset_value'] = Peripheral.__timestamp
                                 for key, val in field_info.items():
                                     if val is not None:
                                         if key == 'field_name':
                                             continue
                                         if key.lower() in VALID_FIELD_KEYS:  # if valid attribute key, apply value to attribute
                                             eval("field.{}(val)".format(key.lower()))
-                                            # logger.debug("%s.peripheral.yaml: field %s %s is %s",
-                                            #              self.hdl_library_name, field.name(), key, str(eval("field.{}()".format(key.lower()))))
+                                            # logger.debug("*.peripheral.yaml: field %s %s is %s",
+                                            #              field.name(), key, str(eval("field.{}()".format(key.lower()))))
                                         else:
-                                            logger.error("Unknown key %s in %s.peripheral.yaml", key, self.hdl_library_name)
+                                            logger.error("Unknown key %s in *.peripheral.yaml", key)
                                             sys.exit()
 
                                     else:
-                                        logger.error("Peripheral '%s': Slave '%s': '%s' key missing value in %s.peripheral.yaml",
-                                                     self.name(), slave_name, key, self.hdl_library_name)
+                                        logger.error("Peripheral '%s': Slave '%s': '%s' key missing value in *.peripheral.yaml",
+                                                     self.name(), slave_name, key)
                                         sys.exit()
 
                                 for key, val in defaults.items():
                                     if field_info.get(key, None) is None:  # if key doesn't exist in config file, apply defaults
                                         eval("field.{}(val)".format(key.lower()))
-                                        logger.debug("%s.peripheral.yaml: Setting field %s key %s to default %s",
-                                                     self.hdl_library_name, field_info['field_name'], str(key), str(val))
+                                        logger.debug("*.peripheral.yaml: Setting field %s key %s to default %s",
+                                                     field_info['field_name'], str(key), str(val))
 
                                 if field.success:
                                     # fields[field_name] = deepcopy(field)
                                     fields.append(deepcopy(field))
                                 else:
-                                    logger.error("%s.peripheral.yaml: field '%s' not succesfully added to fields", self.hdl_library_name, field_name)
+                                    logger.error("*.peripheral.yaml: field '%s' not succesfully added to fields", field_name)
                                     sys.exit()
 
                     if slave_info['slave_type'].upper() in ['RAM', 'FIFO']:
@@ -294,8 +294,8 @@ class Peripheral(BaseObject):
                         self.slaves[-1].update_args({'dual_clock': slave_info['dual_clock']})
 
                 else :
-                    logger.error("Peripheral '%s': Slave '%s': Invalid value %s for 'slave_type' key in %s.peripheral.yaml",
-                                 self.name(), slave_name, slave_info.get('slave_type', 'None'), self.hdl_library_name)
+                    logger.error("Peripheral '%s': Slave '%s': Invalid value %s for 'slave_type' key in *.peripheral.yaml",
+                                 self.name(), slave_name, slave_info.get('slave_type', 'None'))
                     sys.exit()
 
         if 'peripheral_description' in self._config:
@@ -323,7 +323,7 @@ class Peripheral(BaseObject):
                     _val = _val.replace(key1, str(val1))
         # logger.debug("_val={}".format(_val))
         if val is None:
-            logger.error("key set to invalid value %s in %s.peripheral.yaml", _val, self.hdl_library_name)
+            logger.error("key set to invalid value %s in *.peripheral.yaml", _val)
             sys.exit()
 
         if '.coe' in _val:
@@ -333,12 +333,12 @@ class Peripheral(BaseObject):
             if isinstance(result, float):
                 result = int(result)
         except SyntaxError:
-            logger.error("Key set to invalid value '%s' in %s.peripheral.yaml", _val, self.hdl_library_name)
+            logger.error("Key set to invalid value '%s' in *.peripheral.yaml", _val)
             sys.exit()
         except NameError:
             result = _val
             if val_type == int:
-                logger.error("Key set to invalid value '%s' in %s.peripheral.yaml", _val, self.hdl_library_name)
+                logger.error("Key set to invalid value '%s' in *.peripheral.yaml", _val)
                 logger.error("Is parameter defined?")
                 sys.exit()
 
@@ -362,7 +362,7 @@ class Peripheral(BaseObject):
         if protocol is not None and protocol.upper() in ['LITE', 'FULL']:
             register.protocol = protocol.upper()
         # else :
-            # logger.error("{}.peripheral.yaml: Invalid user setting {} for slave {}".format(self.hdl_library_name, protocol, name))
+            # logger.error("*.peripheral.yaml: Invalid user setting {} for slave {}".format(protocol, name))
             # sys.exit()
         self.registers['slave_{}'.format(slave_nr)] = register
         self.slaves.append(register)
@@ -392,10 +392,10 @@ class Peripheral(BaseObject):
         try :
             slave = eval(add_slave)
         except ARGSNameError:
-            logger.error("Invalid slave_name '%s' for %s in %s.peripheral.yaml", name, slave_type, self.hdl_library_name)
+            logger.error("Invalid slave_name '%s' for %s in *.peripheral.yaml", name, slave_type)
             sys.exit()
         except ARGSModeError:
-            logger.error("Invalid access mode for %s '%s' in %s.peripheral.yaml", slave_type, name, self.hdl_library_name)
+            logger.error("Invalid access mode for %s '%s' in *.peripheral.yaml", slave_type, name)
             sys.exit()
         return slave
 
@@ -405,10 +405,10 @@ class Peripheral(BaseObject):
         #  'address_offset', 'reset_value', 'software_value', 'radix', 'field_description']
 
         for field in fields:  # .values():
-            logger.debug("eval_fields= %s", field.name())
+            logger.debug("eval field %s", field.name())
             if [(field.name() == _field.name() and field.group_name() == _field.group_name())for _field in fields].count(True) > 1:
-                logger.error("Field name '%s' group_name '%s' is not unique within slave field list in %s.peripheral.yaml",
-                             field.name(), field.group_name(), self.hdl_library_name)
+                logger.error("Field name '%s' group_name '%s' is not unique within slave field list in *.peripheral.yaml",
+                             field.name(), field.group_name())
                 sys.exit()
             if field.group_name() is not None:
                 field.name(field.group_name() + '_' + field.name())
@@ -559,7 +559,7 @@ class Peripheral(BaseObject):
             for field in fields_eval:
                 if field.address_offset() != UNASSIGNED_ADDRESS:
                     occupied_addresses.append(field.address_offset())
-                    # logger.debug("{}.peripheral.yaml: field {} has manually set address {}".format(self.hdl_library_name, field.name(), str(hex(field.address_offset()))))
+                    # logger.debug("*.peripheral.yaml: field {} has manually set address {}".format(field.name(), str(hex(field.address_offset()))))
 
             # 2nd pass for automatic address and bit offset assignment
             lowest_free_addr = 0
@@ -583,8 +583,8 @@ class Peripheral(BaseObject):
                         lowest_free_addr = lowest_free_addr + WIDTH_IN_BYTES
 
                     if len(set(occupied_bits)) < len(occupied_bits) or any([bit > (DATA_WIDTH-1) or bit < 0 for bit in occupied_bits]):
-                        logger.error('%s.peripheral.yaml: Manually assigned bits for field %s is outside of data width or contains bit collisions',
-                                     self.hdl_library_name, field.name() if field.group_name() is None else "group " + field.group_name())
+                        logger.error('*.peripheral.yaml: Manually assigned bits for field %s is outside of data width or contains bit collisions',
+                                     field.name() if field.group_name() is None else "group " + field.group_name())
                         logger.error("{}".format(str(occupied_bits)))
                         sys.exit()
                 # track beginning of group address
@@ -598,8 +598,8 @@ class Peripheral(BaseObject):
                         if len(group_addresses) == 1:
                             group_address = group_addresses[0]
                         elif len(group_addresses) > 1:
-                            logger.error('%s.peripheral.yaml: Manually set addresses within field group %s %s are conflicting, please change in configuration file',
-                                         self.hdl_library_name, field.group_name(), type(field.group_name()))
+                            logger.error('*.peripheral.yaml: Manually set addresses within field group %s %s are conflicting, please change in configuration file',
+                                         field.group_name(), type(field.group_name()))
                             sys.exit()
                         else:  # address not assigned
                             group_address = UNASSIGNED_ADDRESS
@@ -613,13 +613,13 @@ class Peripheral(BaseObject):
                     while any([i in occupied_bits for i in range(free_bit, free_bit + field.width()+1)]):  # bit is occupied
                         free_bit = free_bit + 1  # try next bit
                         if free_bit == DEFAULT_WIDTH:  # 31 didn't work
-                            logger.error('%s.peripheral.yaml: No suitable gap available for automatic bit offset assignment of field%s',
-                                         self.hdl_library_name, field.name())
+                            logger.error('*.peripheral.yaml: No suitable gap available for automatic bit offset assignment of field%s',
+                                         field.name())
                             logger.error("Check peripheral.yaml file. Field group may be overstuffed or manual bit assignment may have precluded space for other fields")
                             break
                     field.bit_offset(free_bit)
                 occupied_bits = occupied_bits + list(range(field.bit_offset(), field.bit_offset() + field.width()))
-                # logger.warning("{}.peripheral.yaml: Final field {} addr {} [{}-{}]".format(self.hdl_library_name, field.name(), str(field.address_offset()), str(field.bit_offset()+field.width()-1),str(field.bit_offset())))
+                # logger.warning("*.peripheral.yaml: Final field {} addr {} [{}-{}]".format(field.name(), str(field.address_offset()), str(field.bit_offset()+field.width()-1),str(field.bit_offset())))
 
             # re-sort fields to be ordered by address and bit offsets
             fields_eval.sort(key=lambda x: x.address_offset())
@@ -689,8 +689,7 @@ class Peripheral(BaseObject):
                 # if field.number_of_fields() > 1:
                 #    logger.debug("          number_of_fields=%s", str(field.number_of_fields()))
                 logger.debug("          width=%-2s       number_of_fields=%s",
-                            str(field.width()),
-                            str(field.number_of_fields()))
+                             str(field.width()), str(field.number_of_fields()))
 
         # for reg in self.registers.values():
         for reg in self.slaves:
@@ -710,12 +709,9 @@ class Peripheral(BaseObject):
                 #    logger.debug("          number_of_fields=%s", str(field.number_of_fields()))
 
                 logger.debug("          width=%-2s       address_offset=0x%02x  access_mode=%-4s  reset_value=%-4s  radix=%s",
-                            str(field.width()), field.address_offset(),
-                            field.access_mode(), str(field.reset_value()),
-                            field.radix())
+                             str(field.width()), field.address_offset(), field.access_mode(), str(field.reset_value()), field.radix())
                 logger.debug("          bit_offset=%-2s  number_of_fields=%-4s  side_effect=%-4s  software_value=%-4s",
-                            str(field.bit_offset()), str(field.number_of_fields()),
-                            field.side_effect(), str(field.software_value()))
+                             str(field.bit_offset()), str(field.number_of_fields()), field.side_effect(), str(field.software_value()))
 
         logger.debug("  parameters:")
         for param_key, param_val in self._parameters.items():
@@ -797,16 +793,15 @@ class PeripheralLibrary(object):
         for fpn in file_path_names:
             logger.debug("Load peripheral(s) from '%s'", fpn)
             library_config = yaml.load(open(fpn, 'r'))
-            lib = library_config['hdl_library_name']
             for peripheral_config in library_config['peripherals']:
                 try :
-                    peripheral = deepcopy(Peripheral(lib, peripheral_config))
+                    peripheral = deepcopy(Peripheral(peripheral_config))
                 except ARGSNameError:
-                    logger.error("Invalid peripheral_name '%s' in %s.peripheral.yaml",
-                                 peripheral_config['peripheral_name'], lib)
+                    logger.error("Invalid peripheral_name '%s' in *.peripheral.yaml",
+                                 peripheral_config['peripheral_name'], library_config['hdl_library_name'])
                     sys.exit()
                 logger.debug("  read peripheral '%s'" % peripheral.component_name())
-                self.library[lib]['peripherals'].update({peripheral.component_name(): peripheral})
+                self.library[library_config['hdl_library_name']]['peripherals'].update({peripheral.component_name(): peripheral})
                 self.nof_peripherals = self.nof_peripherals + 1
         # self.nof_peripherals = len(self.peripherals)  # number of peripherals
         return
@@ -835,7 +830,7 @@ class PeripheralLibrary(object):
             if matches > 1:
                 if fpga_library is not None and fpga_library in matching_libs:
                     logger.debug("Multiple peripherals named %s found under libs %s, limiting peripheral search to local design library",
-                                periph_name, ' '.join(matching_libs).upper(), fpga_library)
+                                 periph_name, ' '.join(matching_libs).upper(), fpga_library)
                     return self.library[fpga_library]['peripherals'].get(periph_name, None)
                 else:
                     print(' '.join(matching_libs))
@@ -844,7 +839,7 @@ class PeripheralLibrary(object):
                     sys.exit()
             elif matches == 1:
                 logger.debug("Peripheral %s found under library %s",
-                            periph_name, matching_libs[0])
+                             periph_name, matching_libs[0])
                 return self.library[matching_libs[0]]['peripherals'][periph_name]
             else:
                 logger.error("No matching peripherals for '%s' found under %s",
diff --git a/py_args_lib/peripheral_lib/common_func.py b/py_args_lib/peripheral_lib/common_func.py
index bec8a1cb0b51091b705ef65495d799d61443ba48..12bafe574b189c271bd6d63ad2793d6e4141e3d7 100644
--- a/py_args_lib/peripheral_lib/common_func.py
+++ b/py_args_lib/peripheral_lib/common_func.py
@@ -26,6 +26,7 @@
 ################################################################################
 # System imports
 
+import os
 import math    # do not use numpy in common, to avoid making common to elaborate
 import re
 
@@ -61,3 +62,14 @@ def unique(in_list):
 def path_string(dir):
     joined_dir = ''.join(re.split('[/\\\\]+',dir))
     return joined_dir.lower()
+
+
+def check_outdir(boardname, fpganame, args_dir=None):
+    out_dir = os.path.join(os.getenv('ARGS_BUILD_DIR'), boardname.replace('uniboard', 'unb'), 'args', fpganame)
+    if args_dir is not None:
+        out_dir = os.path.join(out_dir, args_dir)
+    try:
+        os.stat(out_dir)  # Check that the output directory exists
+    except FileNotFoundError:
+        os.makedirs(out_dir)  # if not make it
+    return out_dir