From 3e7862e66e2cdbdf5df9be41ea03780dd619464c Mon Sep 17 00:00:00 2001
From: donker <donker@astron.nl>
Date: Fri, 24 Apr 2020 08:51:09 +0200
Subject: [PATCH] L2SDP-69: new script gen_hdl.py is now working, but not for
 avalon.

---
 doc/args_class_diagram.pdf                | Bin 0 -> 31663 bytes
 gen_bus.py                                |  18 +-
 gen_fpgamap_py.py                         |  10 +-
 gen_hdl.py                                | 193 ++++++++++++++++++++++
 gen_slave.py                              |  65 +++-----
 py_args_lib/fpga.py                       |  12 +-
 py_args_lib/peripheral.py                 |  95 +++++------
 py_args_lib/peripheral_lib/common_func.py |  12 ++
 8 files changed, 300 insertions(+), 105 deletions(-)
 create mode 100644 doc/args_class_diagram.pdf
 create mode 100755 gen_hdl.py

diff --git a/doc/args_class_diagram.pdf b/doc/args_class_diagram.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..4d2515387c209b2793e91fc11286ce33b965757f
GIT binary patch
literal 31663
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
Xmn0UIfK#@yfu*4tm#V6(zZ(|-f(D!6

literal 0
HcmV?d00001

diff --git a/gen_bus.py b/gen_bus.py
index 1e0b814..1a01c0e 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 218fa6c..bcf84ac 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 0000000..bc063c9
--- /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 8eb32e7..f0fedc0 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 16247af..3637587 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 cc411fa..e99ea51 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 bec8a1c..12bafe5 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
-- 
GitLab