diff --git a/gen_c_config.py b/gen_c_config.py
deleted file mode 100755
index 96ce0a5ff551e7c22a1afcd1c3ea7a1514901186..0000000000000000000000000000000000000000
--- a/gen_c_config.py
+++ /dev/null
@@ -1,248 +0,0 @@
-#! /usr/bin/env python3
-# ##########################################################################
-# Copyright 2020
-# ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
-# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-# ##########################################################################
-
-# ##########################################################################
-#   Author           Date      Version comments
-#   Keith Bengsotn   Nov 2018  Original
-#   Pieter Donker    Apr 2020  add logging and changed output path
-#
-###############################################################################
-
-"""
-    Generate Address Map for usage in C software.
-    Lists every 'field' in the fpga.yaml and peripheral.yaml files
-    ordered by address in the address map.
-"""
-
-import sys
-import os
-from argparse import ArgumentParser
-
-from args_logger import MyLogger
-from py_args_lib import RAM, FIFO, Register, FPGALibrary, ceil_pow2, WORD_SIZE
-
-
-def gen_c_config(fpga, fpga_name, out_dir):
-    """
-    Generate adderss map
-        fpga: all fpga.yaml files found in system
-        fpga_name: the particular fpga we're interested in
-        out_dir: the directory in which output should be written
-    """
-    out = []  # list to hold text lines to be written to output .ccfg file
-    out.append("# Peripherals for {}:".format(fpga_name))
-    field_count = 0  # count of number of fields found in FPGA address map
-    for periph_name, periph_info in fpga.address_map.items():
-        logger.debug('periph_name=%s', periph_name)
-        peripheral = periph_info['peripheral']
-        p_num = int(periph_info['periph_num'])
-        pname = peripheral.name()
-        logger.debug('pname=%s', pname)
-        count = peripheral.number_of_peripherals()
-        base = int(periph_info['base'] / WORD_SIZE)
-        typ = periph_info['type']
-        
-        if use_avalon:
-            span = max(int(8 / WORD_SIZE), ceil_pow2(int(periph_info['span'] / WORD_SIZE)))  # span can be incorrect (bug in pyargslib?)
-            txt = '#  peripheral={} start=0x{:04X} span=0x{:04X} count={:02} idx={} stop=0x{:04X}'.format(
-                  pname, base, span, count, p_num, base + count * span)
-        else:    
-            span = int(periph_info['span'] / WORD_SIZE)  # span can be incorrect (bug in pyargslib?)
-            txt = '#  peripheral={} start=0x{:04X} span=0x{:04X} type={} count={:02} idx={} stop=0x{:04X}'.format(
-                  pname, base, span, typ, count, p_num, base + count * span)
-        
-        out.append(txt)
-
-        mmport = periph_info['mmport']
-        num_mm_ports = mmport.number_of_mm_ports()
-        mm_port_name = mmport.name()
-        if isinstance(mmport, RAM):
-            if mmport.user_defined_name():
-                out.append('#   RAM-SLAVE={:20}'.format(mmport.user_defined_name()))
-            else:
-                out.append('#   RAM-SLAVE={:20}'.format(mmport.name()))
-            
-            ram_base = base
-            ram_len = int(mmport.address_length() / WORD_SIZE)
-            ram_name = 'data'
-            access = mmport.access_mode()
-#            width = mmport.width() # Should be mmport.user_width() ??
-            for i in range(0, num_mm_ports):
-                txt = ['      BlockRAM   0x{:08X} len={} {}'.format(
-                       ram_base + i * ram_len, ram_len, access)]
-
-                if p_num == 0:
-                    txt.append('{}'.format(pname))
-                else:
-                    txt.append('{}[{}]'.format(pname, p_num))
-
-                if num_mm_ports == 1:
-                    txt.append('{} {}'.format(mm_port_name, ram_name))
-                else:
-                    txt.append('{}[{}] {}'.format(mm_port_name, i, ram_name))
-
-                out.append(' '.join(txt))
-
-                field_count += 1
-        elif isinstance(mmport, FIFO):
-            if mmport.user_defined_name():
-                out.append('#   FIFO-SLAVE={:20}'.format(mmport.user_defined_name()))
-            else:
-                out.append('#   FIFO-SLAVE={:20}'.format(mmport.name()))
-            fifo_base = base
-            fifo_len = int(mmport.address_length() / WORD_SIZE)
-            fifo_name = 'data'
-            access = mmport.access_mode()
-#            width = mmport.width() # Should be mmport.user_width() ??
-            for i in range(0, num_mm_ports):
-                txt = ['      FIFO       0x{:08X} len={} {}'.format(
-                       fifo_base + i * fifo_len, fifo_len, access)]
-                if p_num == 0:
-                    txt.append('{}'.format(pname))
-                else:
-                    txt.append('{}[{}]'.format(pname, p_num))
-                if num_mm_ports == 1:
-                    txt.append('{} {}'.format(mm_port_name, fifo_name))
-                else:
-                    txt.append('{}[{}] {}'.format(mm_port_name, i, fifo_name))
-                out.append(' '.join(txt))
-                field_count += 1
-        elif isinstance(mmport, Register):
-            
-            if mmport.user_defined_name():
-                out.append('#   REG-SLAVE={} no.mm_ports={} len={} (base=0x{:X})'.format(
-                    mmport.user_defined_name(),
-                    mmport.number_of_mm_ports(),
-                    int(mmport.address_length() / WORD_SIZE),
-                    int(mmport.base_address() / WORD_SIZE)))
-                    #int(mmport.base_address() / WORD_SIZE)))
-            else:
-                out.append('#   REG-SLAVE={} no.mm_ports={} len={} (base=0x{:X})'.format(
-                    mmport.name(), mmport.number_of_mm_ports(),
-                    int(mmport.address_length() / WORD_SIZE),
-                    int(mmport.base_address() / WORD_SIZE)))
-                    #int(mmport.base_address() / WORD_SIZE)))
-            
-            # Fields that have a non-unity 'number_of_fields' specifier may
-            #   become RAM at the start of the MM port instances
-            for ram in mmport.rams:
-                ram_base = int(ram.base_address() / WORD_SIZE) + base
-                ram_len = int(ram.number_of_fields())
-                ram_name = ram.name()
-                access = ram.access_mode()
-                for i in range(0, num_mm_ports):
-                    txt = ['      DistrRAM   0x{:08X} len={} {}'.format(
-                        ram_base + i * ram_len, ram_len, access)]
-                    if p_num == 0:
-                        txt.append('{}'.format(pname))
-                    else:
-                        txt.append('{}[{}]'.format(pname, p_num))
-                    if num_mm_ports == 1:
-                        txt.append('{} {}'.format(mm_port_name, ram_name))
-                    else:
-                        txt.append('{}[{}] {}'.format(mm_port_name, i, ram_name))
-                    out.append(' '.join(txt))
-                    field_count += 1
-#                if num_mm_ports == 1:
-#                    out.append('      DistrRAM   0x{:08X} len={} {} {} {} {}'.format(
-#                        ram_base, ram_len,access,pname,mm_port_name, ram_name))
-#                else:
-#                    for i in range(0,num_mm_ports):
-#                        out.append('      DistrRAM   0x{:08X} len={} {} {} {} {}[{}]'.format(
-#                             ram_base+i*ram_len, ram_len,access,pname, mm_port_name, ram_name, i))
-
-            field_base = base
-            if mmport.rams:
-                field_base += int(mmport.base_address() / WORD_SIZE)
-
-            # All other fields (with unity 'number_of_fields' attribute)
-            mm_port_length = int(mmport.address_length() / WORD_SIZE)
-            for i in range(0, num_mm_ports):
-                for fld in mmport.fields:
-                    bit_lo = fld.bit_offset()
-                    bit_hi = bit_lo + fld.width() - 1
-                    # field_base = f.base_address
-                    field_offset = int(fld.address_offset() / WORD_SIZE)
-                    field_name = fld.name()
-                    access = fld.access_mode()
-
-                    txt = ['      BitField   0x{:08X} b[{}:{}] {}'.format(
-                        field_offset + field_base + i * mm_port_length, bit_hi, bit_lo, access)]
-                    if p_num == 0:
-                        txt.append('{}'.format(pname))
-                    else:
-                        txt.append('{}[{}]'.format(pname, p_num))
-                    if num_mm_ports == 1:
-                        txt.append('{} {}'.format(mm_port_name, field_name))
-                    else:
-                        txt.append('{}[{}] {}'.format(mm_port_name, i, field_name))
-                    out.append(' '.join(txt))
-                    field_count += 1
-
-    # Write all text lines held in list 'out' to file
-    output_filename = os.path.join(out_dir, '{}.ccfg'.format(fpga_name))
-    with open(output_filename, 'w') as out_file:
-        out_file.write('\n'.join(out))
-
-    logger.info("Found %d fields in FPGA '%s'", field_count, fpga_name)
-    logger.info('Wrote: %s', output_filename)
-
-
-def check_out_dir(boardname, args_dir=None):
-    out_dir = os.path.join(os.getenv('ARGS_BUILD_DIR'), boardname.replace('uniboard', 'unb'), 'args', args.fpga)
-    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
-
-
-if __name__ == '__main__':
-
-    parser = ArgumentParser(description='ARGS tool script to generate [fpga_name].ccfg M&C C client include file')
-    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
-
-    # 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))
-
-    # Find and parse all *.fpga.yaml YAML files under root directory for ARGS_WORK
-    # 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
-    libRootDir = os.path.expandvars('$ARGS_WORK')
-    fpga_lib = FPGALibrary(root_dir=libRootDir, use_avalon_base_addr=use_avalon)
-    fpga = fpga_lib.get_fpga(fpga_name)
-
-    # create the summary file in the output directory
-    out_dir = check_out_dir(fpga.board_name, 'c')
-    gen_c_config(fpga, fpga_name, out_dir)
diff --git a/gen_doc.py b/gen_doc.py
index b0b65ed75777f76324bd1cd1af9325fd535d1eaf..4e98652834f6a5dd41ef50a593acae15aff1203a 100755
--- a/gen_doc.py
+++ b/gen_doc.py
@@ -79,7 +79,7 @@ def gen_reg_tables(subsection, group_fields):
         if (bit_bounds[-1] - last_bit_boundary) > 16:
             bit_bounds.append(bit_bounds[-1] - 16)  # still limit to max of  16
             last_bit_boundary = bit_bounds[-1]
-        # print("field {} upper {} lower {}".format(field.name(), str(field.bit_offset() + field.width() -1), str(field.bit_offset())))
+        # print("field {} upper {} lower {}".format(field.name(), str(field.bit_offset() + field.mm_width() -1), str(field.bit_offset())))
         # print(*bit_bounds)
     if bit_bounds[-1] != 0:
         bit_bounds.append(0)
@@ -103,7 +103,7 @@ def gen_reg_tables(subsection, group_fields):
         # print("Table {} bounding {} and {}".format(str(i),str(gap_bit + 1), str(end_bit)))
         for field in group_fields[::-1]:
             field_name = field.name() if field.group_name() is None else field.name().split(field.group_name() + '_')[-1]
-            upper_bit = field.bit_offset() + field.width() - 1  # inclusive
+            upper_bit = field.bit_offset() + field.mm_width() - 1  # inclusive
             # print("field {} has upper bit {} gap bit is {}".format(field_name,str(upper_bit), str(gap_bit)))
             if upper_bit < gap_bit:
                 gap_width = min(gap_bit - upper_bit, nof_cols - 1)
@@ -116,8 +116,8 @@ def gen_reg_tables(subsection, group_fields):
             # print("field {} bit offset {} should be more or equal to {} and upper bit {} should be less than {}".format(
             #       field_name, str(field.bit_offset()), str(bit_bounds[i]), str(upper_bit), str(bit_bounds[i-1])))
             if field.bit_offset() >= end_bit and upper_bit < bit_bounds[i - 1]:  # field fully contained
-                col_list.append(MultiColumn(field.width(), align='|c|', data=field_name))
-                # print("added complete field {} of width {}".format(field_name, str(field.width())))
+                col_list.append(MultiColumn(field.mm_width(), align='|c|', data=field_name))
+                # print("added complete field {} of width {}".format(field_name, str(field.mm_width())))
                 gap_bit = field.bit_offset() - 1
             elif upper_bit >= end_bit and field.bit_offset() < end_bit:  # upper partial
                 col_list.append(MultiColumn(upper_bit - bit_bounds[i] + 1, align='|c|', data=field_name))
@@ -272,7 +272,7 @@ class FPGADocumentation(object):
                 periph_reg_table.add_row((mmport.base_address(), '', mmport.address_length(), mmport.name(), mmport.number_of_mm_ports()))
                 periph_reg_table.add_hline()
                 for ram in mmport.rams:
-                    periph_reg_table.add_row(('', ram.address_offset(), ram.number_of_fields() * WORD_SIZE, '>>  {} (RAM)'.format(ram.name()), mmport.number_of_mm_ports()))
+                    periph_reg_table.add_row(('', ram.address_offset(), ram.number_of_fields() * MM_BUS_SIZE, '>>  {} (RAM)'.format(ram.name()), mmport.number_of_mm_ports()))
                     periph_reg_table.add_hline()
                 added_field_groups = []
 
@@ -315,14 +315,14 @@ class FPGADocumentation(object):
                         group_subsection = gen_reg_tables(group_subsection, group_fields)
                         for field in group_fields[::-1]:
                             field_name = field.name() if field.group_name() is None else field.name().split(field.group_name() + '_')[-1]
-                            bit_string = "Bit {}".format(str(field.bit_offset())) if field.width() == 1 else "Bits {}:{}".format(str(field.bit_offset() + field.width() - 1), str(field.bit_offset()))
+                            bit_string = "Bit {}".format(str(field.bit_offset())) if field.mm_width() == 1 else "Bits {}:{}".format(str(field.bit_offset() + field.mm_width() - 1), str(field.bit_offset()))
                             group_subsection.append(bold("{}\t\t{} ({}):".format(bit_string, field_name, field.access_mode())))
                             group_subsection.append("\t\t{}".format(field.field_description()))
                             group_subsection.append(NewLine())
                             group_subsection.append(NewLine())
                         mm_port_subsection.append(group_subsection)
             else:  # RAM or FIFO
-                mm_port_subsection.append("Data width: {}".format(mmport.width()))
+                mm_port_subsection.append("Data width: {}".format(mmport.mm_width()))
                 mm_port_subsection.append(NewLine())
                 if isinstance(mmport, RAM):
                     mm_port_subsection.append("User data width: {}".format(mmport.user_width()))
@@ -421,7 +421,7 @@ class PeripheralDocumentation(object):
 
                 if isinstance(mmport, Register):  # expand registers and fields
                     for ram in mmport.rams:
-                        periph_reg_table.add_row((str(ram.base_address()), str(ram.number_of_fields() * WORD_SIZE), ram.name() + ' (RAM)', str(mmport.number_of_mm_ports())))
+                        periph_reg_table.add_row((str(ram.base_address()), str(ram.number_of_fields() * MM_BUS_SIZE), ram.name() + ' (RAM)', str(mmport.number_of_mm_ports())))
                         periph_reg_table.add_hline()
                     periph_reg_table.add_row((str(mmport.base_address()), str(mmport.address_length()), mmport.name(), str(mmport.number_of_mm_ports())))
                     periph_reg_table.add_hline()
@@ -467,14 +467,14 @@ class PeripheralDocumentation(object):
                             group_subsection = gen_reg_tables(group_subsection, group_fields)
                             for field in group_fields[::-1]:
                                 field_name = field.name() if field.group_name() is None else field.name().split(field.group_name() + '_')[-1]
-                                bit_string = "Bit {}".format(str(field.bit_offset())) if field.width() == 1 else "Bits {}:{}".format(str(field.bit_offset() + field.width() - 1), str(field.bit_offset()))
+                                bit_string = "Bit {}".format(str(field.bit_offset())) if field.mm_width() == 1 else "Bits {}:{}".format(str(field.bit_offset() + field.mm_width() - 1), str(field.bit_offset()))
                                 group_subsection.append(bold("{}\t\t{} ({}):".format(bit_string, field_name, field.access_mode())))
                                 group_subsection.append("\t\t{}".format(field.field_description()))
                                 group_subsection.append(NewLine())
                                 group_subsection.append(NewLine())
                             mm_port_subsection.append(group_subsection)
                 else:  # RAM or FIFO
-                    mm_port_subsection.append("Data width: {}".format(mmport.width()))
+                    mm_port_subsection.append("Data width: {}".format(mmport.mm_width()))
                     mm_port_subsection.append(NewLine())
                     if isinstance(mmport, RAM):
                         mm_port_subsection.append("User data width: {}".format(mmport.user_width()))
diff --git a/gen_fpgamap_py.py b/gen_fpgamap_py.py
deleted file mode 100755
index 23eb163580448544e5368b25f2d7dd745c9affad..0000000000000000000000000000000000000000
--- a/gen_fpgamap_py.py
+++ /dev/null
@@ -1,200 +0,0 @@
-#! /usr/bin/env python3
-# ##########################################################################
-# Copyright 2020
-# ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
-# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-# ##########################################################################
-
-# ##########################################################################
-#   Author           Date      Version comments
-#   John Matthews    Dec 2017  Original
-#   Pieter Donker    Mrt 2020  add logging
-#
-###############################################################################
-
-"""
-    Generate fpgamap.py M&C Python client include file
-"""
-
-import sys
-import os
-import logging
-from argparse import ArgumentParser
-from args_logger import MyLogger
-from py_args_lib import FPGA, RAM, FIFO, Register, PeripheralLibrary, FPGALibrary, ceil_pow2
-import pprint
-# import code
-
-
-def genPython(fpga, fpgaName, addr_size=4, readable=False):
-    mmPorts = {}
-    print("Including MM ports for {}:".format(fpgaName))
-    for mmPortName, mmPortInfo in fpga.address_map.items():
-        if mmPortInfo['periph_num'] > 0:
-            continue
-        peripheral = mmPortInfo['peripheral']
-        mmport = mmPortInfo['mm_port']
-        base = int(mmPortInfo['base'] / addr_size)  # Convert from AXI byte address to register address
-        if peripheral.name() not in mmPorts:
-            mmPorts[peripheral.name()] = {'mm_ports': {}, 'start': base, 'span': int(mmPortInfo['span'] / addr_size), 'count': peripheral.number_of_peripherals()}
-        else:
-            mmPorts[peripheral.name()]['span'] += int(mmPortInfo['span'] / addr_size)
-        mm_ports = mmPorts[peripheral.name()]['mm_ports']
-        mmPortOffset = base - mmPorts[peripheral.name()]['start']
-        if isinstance(mmport, RAM):
-            print(' {} at 0x{:X}'.format(mmPortName, base))
-            # Note py_args_lib quirk. RAM mm_ports include a single named field. There doesn't seem to be any way to recover this
-            # from py_args_lib at present. I shall assume that this field is always called "data"
-            mm_ports[mmport.name()] = {'type': 'RAM',
-                                    'start': mmPortOffset,
-                                    'step': mmport.number_of_fields(),
-                                    'stop': mmPortOffset+mmport.number_of_fields()*mmport.number_of_mm_ports(),
-                                    'fields': {'data': {'start': 0,
-                                                        'step': 1,
-                                                        'stop': mmport.number_of_fields(),
-                                                        'access_mode': mmport.access_mode(),
-                                                        'width': mmport.width(),
-                                                        'default': mmport.reset_value(),
-                                                        'description': mmport.field_description(),
-                                                        'bit_offset': mmport.bit_offset()}
-                                               }
-                                    }
-            # code.interact(local=locals())
-        elif isinstance(mmport, FIFO):
-            print(' {} at 0x{:X}'.format(mmPortName, base))
-            # Assume one field per FIFO MM port
-            # Assume one field per word
-            mm_ports[mmport.name()] = {'type': 'FIFO',
-                                    'start': mmPortOffset,
-                                    'step': 1,
-                                    'stop': mmPortOffset+mmport.number_of_mm_ports(),
-                                    'fields': {'data': {'start': 0,
-                                                        'step': 1,
-                                                        'stop': 1,
-                                                        'access_mode': mmport.access_mode(),
-                                                        'width': mmport.width(),
-                                                        'default': mmport.reset_value(),
-                                                        'description': mmport.field_description(),
-                                                        'bit_offset': mmport.bit_offset(),
-                                                        'depth': mmport.number_of_fields()}
-                                               }
-                                    }
-        elif isinstance(mmport, Register):
-            print(' {} at 0x{:X}'.format(mmPortName, base))
-            fields = {}
-            minOffset = 2**32
-            maxOffset = 0
-            for r in mmport.rams:
-                offset = int(r.base_address()/addr_size) - mmPortOffset
-                stop_address = offset + (mmport.number_of_mm_ports()-1)*ceil_pow2(r.number_of_fields())+r.number_of_fields()
-                fields[r.name()] = {'start': offset,
-                                    'step': 1,
-                                    'stop': stop_address,
-                                    'access_mode': r.access_mode(),
-                                    'width' : r.width(),
-                                    'default': r.reset_value(),
-                                    'description': r.field_description(),
-                                    'bit_offset': 0}
-                minOffset = min(minOffset, offset)
-                maxOffset = max(maxOffset, stop_address)
-            for f in mmport.fields:
-                # Note py_args_lib quirk. When number_of_fields > 1 then numeric field id is added to string field name
-                offset = int(f.address_offset()/addr_size) + int(mmport.base_address()/addr_size) - mmPortOffset
-
-                fields[f.name()] = {'start': offset,
-                                    'step': 1,
-                                    'stop': offset+1,
-                                    'access_mode': f.access_mode(),
-                                    'width': f.width(),
-                                    'default': f.reset_value(),
-                                    'description': f.field_description(),
-                                    'bit_offset': f.bit_offset()}
-                minOffset = min(minOffset, offset)
-                maxOffset = max(maxOffset, offset+1)
-            mm_ports[mmport.name()] = {'type': 'REG',
-                                    'start': mmPortOffset,
-                                    'step': maxOffset,
-                                    'stop': mmPortOffset+mmport.number_of_mm_ports()*(maxOffset),
-                                    'fields': fields}
-
-    map = {}
-    for k, v in mmPorts.items():
-        map[k] = {'start': v['start'], 'step': v['span'], 'stop': v['start']+v['count']*v['span'], 'mm_ports': v['mm_ports']}
-
-    pp = pprint.PrettyPrinter(width=300)
-    mapStr = pp.pformat(map) if readable else str(map)
-
-    out_dir = os.path.join(os.getenv('ARGS_BUILD_DIR'), fpga.board_name.replace('uniboard','unb'), 'args', fpgaName, 'py')
-    try:
-        os.stat(out_dir)
-    except FileNotFoundError:
-        os.makedirs(out_dir)
-
-    # args_dir = os.path.expandvars('$HDL_BUILD_DIR/ARGS')
-    # try:
-    #     os.stat(args_dir)
-    # except:
-    #     os.mkdir(args_dir)
-    # out_dir = os.path.join(args_dir, 'py')
-    # try:
-    #     os.stat(out_dir)
-    # except:
-    #     os.mkdir(out_dir)
-    # out_dir = os.path.join(out_dir, fpgaName)
-    # try:
-    #     os.stat(out_dir)
-    # except:
-    #     os.mkdir(out_dir)
-
-    with open(os.path.join(out_dir, 'fpgamap.py'), 'wt') as file:
-        file.write("# M&C Python client include file for {} FPGA\n".format(fpgaName))
-        file.write("# Note that this file uses register (not AXI byte) addresses and offsets\n".format(fpgaName))
-        file.write("FPGAMAP = " + mapStr + "\n")
-
-
-if __name__ == '__main__':
-
-    parser = ArgumentParser(description='ARGS tool script to generate fpgamap.py M&C Python client include file')
-    parser.add_argument('-f', '--fpga', required=True, help='ARGS fpga_name')
-    parser.add_argument('-a', '--avalon', action='store_true', default=False, help="use config file qsys/sopc for base addresses")
-    parser.add_argument('-r', '--readable', action='store_true', help='Generate human readable map file')
-    parser.add_argument('-s', '--size', type=int, default=4, help='size per address in bytes')
-    parser.add_argument('-v', '--verbosity', default='INFO', help="stdout log level can be [ERROR | WARNING | INFO | DEBUG]")
-    args = parser.parse_args()
-
-    fpgaName = args.fpga
-    useAvalon = args.avalon
-    readable = args.readable
-    addr_size = args.size
-
-    libRootDir = os.path.expandvars('$ARGS_WORK')
-
-    # 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))
-
-    # Find and parse all *.fpga.yaml YAML files under libRootDir
-    # 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
-    fpga_lib = FPGALibrary(root_dir=libRootDir, use_avalon_base_addr=useAvalon)
-    fpga = fpga_lib.get_fpga(fpgaName)
-
-    genPython(fpga, fpgaName, addr_size, readable)
diff --git a/gen_rom_mmap.py b/gen_rom_mmap.py
index 8abe78687c9b56995687657673cbdfd2f7995929..503d8aaffb64742a0032bd4cf95ef37edaa67c85 100755
--- a/gen_rom_mmap.py
+++ b/gen_rom_mmap.py
@@ -20,18 +20,30 @@
 # ##########################################################################
 #   Author           Date       Version comments
 #   Pieter Donker    June 2020  first version
+#   Eric Kooistra    March 2021 support updated mmap format to suit the
+#                               register map for lofar2_unb2b_beamformer
 #
 ###############################################################################
 
 """
     Generate fpgamap file to copy in rom_system  [fpga_name].mmap
-    the file for the ROM is selected in radiohdl/run_reg script .
+    the file for the ROM is selected in radiohdl/run_reg script.
 
     Usage:
     > gen_rom_mmap.py -h
     > gen_rom_mmap.py -f lofar2_unb2b_beamformer -v DEBUG
     > gen_rom_mmap.py -f unb2b_minimal
     > gen_rom_mmap.py -f lofar2_unb2b_beamformer
+
+    View result:
+    > more hdl/build/unb2b/args/lofar2_unb2b_beamformer/c/lofar2_unb2b_beamformer.mmap
+
+    Verify result:
+    > diff hdl/build/unb2b/args/lofar2_unb2b_beamformer/c/lofar2_unb2b_beamformer.mmap hdl/applications/lofar2/designs/lofar2_unb2b_beamformer/lofar2_unb2b_beamformer.mmap.gold
+
+    Manually update lofar2_unb2b_beamformer.mmap.gold reference mmap file and
+    save in git, whenever the register map is changed, to keep it as a test
+    case for ARGS code verification).
 """
 
 import sys
@@ -39,23 +51,46 @@ import os
 import logging
 from argparse import ArgumentParser
 from args_logger import MyLogger
-from py_args_lib import FPGA, RAM, FIFO, Register, PeripheralLibrary, FPGALibrary, WORD_SIZE
+from py_args_lib import FPGA, RAM, FIFO, Register, PeripheralLibrary, FPGALibrary, MM_BUS_SIZE
 import pprint
+from common_func import ceil_div, smallest
+
 
+def _make_mask(mm_width, bit_offset=0, user_width=0, user_word_order='le'):
+    """Derive MM word bit mask and optional user word bit mask in case it differs from the MM word bit mask.
 
-def _make_mask(field_width, radix_width=0, bit_offset=0):
-    """Derive radix bit mask and number of radix elements per field."""
-    _nof_radix_per_field = 1
-    if radix_width == 0 or radix_width == field_width:
-        _hi_bit = bit_offset + field_width - 1
+       Remarks:
+       - To ease parsing, use [hi:lo] format also for [i:i] = [i], because the bit mask then always contain a colon ':'.
+       - For user_width > mm_width use one user_mask string per MM word
+    """
+    if user_width == None or user_width == mm_width:
+        # user_mask = mm_mask, then use redundant user_mask string
+        _mm_mask_strings = ['b[{}:{}]'.format(bit_offset + mm_width - 1, bit_offset)]
+        _user_mask_strings = ['-']
     else:
-        if radix_width < field_width:
-            _nof_radix_per_field = field_width // radix_width
-        _hi_bit = bit_offset + radix_width - 1
-    _lo_bit = bit_offset
-    _packing_str = '{}'.format(_nof_radix_per_field)
-    _bit_mask_str = 'b[{}:{}]'.format(_hi_bit, _lo_bit)  # To ease parsing, use [hi:lo] format also for [i:i] = [i], because the bit mask then always contain a colon ':'.
-    return _packing_str, _bit_mask_str
+        # user_mask != mm_mask, then force bit_offset = 0
+        if user_width < mm_width:
+            # pack multiple user words per MM word
+            _mm_mask_strings = ['b[{}:{}]'.format(mm_width - 1, 0)]
+            _user_mask_strings = ['b[{}:{}]'.format(user_width - 1, 0)]
+        else:
+            # use seperate user_mask string per MM word
+            _mm_mask_strings = []
+            _user_mask_strings = []
+            _wi = 0
+            _user_lo = 0
+            while _user_lo < user_width:
+                _user_hi = smallest((_wi + 1) * mm_width - 1, user_width - 1)
+                _mm_hi = _user_hi % mm_width
+                _mm_mask_strings.append('b[{}:{}]'.format(_mm_hi, 0))
+                _user_mask_strings.append('b[{}:{}]'.format(_user_hi, _user_lo))
+                _wi += 1
+                _user_lo = _wi * mm_width
+            # default is little endian word order, swap for big endian word order
+            if user_word_order == 'be':
+                _mm_mask_strings.reverse()
+                _user_mask_strings.reverse()
+    return _mm_mask_strings, _user_mask_strings
 
 
 def gen_fpga_map(fpga, fpga_name):
@@ -63,7 +98,7 @@ def gen_fpga_map(fpga, fpga_name):
 
     # pprint.pprint(fpga.address_map)
     print("Including MM ports for {}:".format(fpga_name))
-    map_format_str = '  {:24s}  {:4s}  {:4s}  {:5s}  {:40s}  0x{:08x}  {:6d}  {:>5s}  {:>11s}  {:>5s}  {:>10s}'
+    map_format_str = '  {:24s}  {:4s}  {:4s}  {:5s}  {:40s}  0x{:08x}  {:>6s}  {:>5s}  {:>11s}  {:>10s}  {:>10s}'
     map_nof_columns = len(map_format_str.split('{')) - 1
     for mm_port_name, mm_port_info in fpga.address_map.items():
 
@@ -82,17 +117,23 @@ def gen_fpga_map(fpga, fpga_name):
 
         number_of_mm_ports = str(mm_port.number_of_mm_ports())
         base               = int(mm_port_info['base'])
-        base_word          = int(base / WORD_SIZE)
+        base_word          = int(base / MM_BUS_SIZE)
 
-        # EK TODO: new structured naming: mm_port_name = mm_port_name + peripheral_group ~= user_def_name
         if isinstance(mm_port, RAM):
             print(' RAM  {:80s} at 0x{:08x}(bytes) 0x{:04x}(words)  "{}"'.format(mm_port_name, base, base_word, user_def_name))
 
-            # EK: TODO should get radix info from field and use _make_mask().
-            radix = '-'
-            radix_packing = '-'
-            radix_bit_mask = '-' 
-            
+            #print('{}: {}, {}, {}'.format(field_name, f.radix().lower(), f.user_width(), f.resolution_w()))
+
+            # EK TODO : 'RAM' object has no attribute 'fields', it should have because RAM and FIFO are no so different than REG, or there is only
+            #           one field, so therefore no need for fields level. RAM , FIFO are a Field, REG uses one or more Field
+
+            #print('{}: {}, {}, {}'.format(mm_port.name(), mm_port.number_of_fields(), mm_port.mm_width(), mm_port.user_width()))
+
+            number_of_fields = str(mm_port.number_of_fields())
+
+            radix = mm_port.radix()
+            mm_masks, user_masks = _make_mask(mm_port.mm_width(), mm_port.bit_offset(), mm_port.user_width(), mm_port.user_word_order())
+
             _map_str.append(map_format_str.format(
                             user_def_name,
                             number_of_peripherals,
@@ -100,21 +141,37 @@ def gen_fpga_map(fpga, fpga_name):
                             'RAM',
                             'data',   # EK: TODO should come from yaml field_name
                             base_word,
-                            mm_port.number_of_fields(),
+                            number_of_fields,
                             mm_port.access_mode(),
                             radix,
-                            radix_packing,
-                            radix_bit_mask
+                            mm_masks[0],
+                            user_masks[0]
                             ))
 
+            # write second MM word incase user_width > mm_width
+            for wi in range(1, len(user_masks)):
+                _map_str.append(map_format_str.format(
+                                '-',
+                                '-',
+                                '-',
+                                '-',
+                                '-',
+                                f_base + wi,
+                                '-',
+                                '-',
+                                '-',
+                                mm_masks[wi],
+                                user_masks[wi],
+                                ))
+
         elif isinstance(mm_port, FIFO):
             print(' FIFO {:80s} at 0x{:08x}(bytes) 0x{:04x}(words)  "{}"'.format(mm_port_name, base, base_word, user_def_name))
-            
-            # EK: TODO should get radix info from field and use _make_mask().
-            radix = '-'
-            radix_packing = '-'
-            radix_bit_mask = '-' 
-            
+
+            number_of_fields = str(mm_port.number_of_fields())
+
+            radix = mm_port.radix()
+            mm_masks, user_masks = _make_mask(mm_port.mm_width(), mm_port.bit_offset(), mm_port.user_width(), mm_port.user_word_order())
+
             _map_str.append(map_format_str.format(
                             user_def_name,
                             number_of_peripherals,
@@ -122,22 +179,38 @@ def gen_fpga_map(fpga, fpga_name):
                             'FIFO',
                             'data',   # EK: TODO should come from yaml field_name
                             base_word,
-                            mm_port.number_of_fields(),
+                            number_of_fields,
                             mm_port.access_mode(),
                             radix,
-                            radix_packing,
-                            radix_bit_mask
+                            mm_masks[0],
+                            user_masks[0]
                             ))
 
+            # write second MM word incase user_width > mm_width
+            for wi in range(1, len(user_masks)):
+                _map_str.append(map_format_str.format(
+                                '-',
+                                '-',
+                                '-',
+                                '-',
+                                '-',
+                                f_base + wi,
+                                '-',
+                                '-',
+                                '-',
+                                mm_masks[wi],
+                                user_masks[wi],
+                                ))
+
         elif isinstance(mm_port, Register):
             mm_port_type       = "REG"   # EK: TODO get mm_port_type from mm_port ?
             print(' REG  {:80s} at 0x{:08x}(bytes) 0x{:04x}(words)  "{}"'.format(mm_port_name, base, base_word, user_def_name))
             done = []
             for f in mm_port.fields:
                 field_name = f.name()
-                
-                #print('{}: {}, {}, {}'.format(field_name, f.radix().lower(), f.radix_width(), f.radix_resolution()))
-                
+
+                #print('{}: {}, {}, {}'.format(field_name, f.radix().lower(), f.user_width(), f.resolution_w()))
+
                 # EK: TODO the check on f.number_of_fields() and on done should not be necessary, because the array of fields should only have one element in mm_port.fields. The mm_port.number_of_fields() should not have been expanded in mm_port.
                 if f.number_of_fields() > 1:
                     field_name = f.name().strip('0123456789')  # strip field array index
@@ -145,17 +218,21 @@ def gen_fpga_map(fpga, fpga_name):
                     continue
                 if field_name not in done:
                     done.append(field_name)
-                    
+
                 #field_group = f.group_name()
                 #if field_group == None:
                 #    field_group = '-'
-                    
-                f_base = base_word + int(f.address_offset() / WORD_SIZE)
-                
-                # EK: TODO support parameter passing for radix_width, now the parameter string seems to be passed instead of the parameter evaluation
-                #print('{}: {}, {}, {}'.format(field_name, f.width(), f.radix_width(), f.bit_offset()))
-                radix_packing, radix_bit_mask = _make_mask(f.width(), f.radix_width(), f.bit_offset())
 
+                f_base = base_word + int(f.address_offset() / MM_BUS_SIZE)
+
+                number_of_fields = str(f.number_of_fields())
+
+                # EK: TODO support parameter passing for user_width, now the parameter string seems to be passed instead of the parameter evaluation
+                # EK: TODO support default user_width = None --> user_width = mm_width
+                #print('{}: {}, {}, {}'.format(field_name, f.mm_width(), f.user_width(), f.bit_offset()))
+                mm_masks, user_masks = _make_mask(f.mm_width(), f.bit_offset(), f.user_width(), f.user_word_order())
+
+                # write field info and first MM word
                 _map_str.append(map_format_str.format(
                                 user_def_name,
                                 number_of_peripherals,
@@ -163,13 +240,29 @@ def gen_fpga_map(fpga, fpga_name):
                                 mm_port_type,
                                 field_name,
                                 f_base,
-                                f.number_of_fields(),
+                                number_of_fields,
                                 f.access_mode(),
                                 f.radix(),
-                                radix_packing,
-                                radix_bit_mask
+                                mm_masks[0],
+                                user_masks[0],
                                 ))
-                
+
+                # write second MM word incase user_width > mm_width
+                for wi in range(1, len(user_masks)):
+                    _map_str.append(map_format_str.format(
+                                    '-',
+                                    '-',
+                                    '-',
+                                    '-',
+                                    '-',
+                                    f_base + wi,
+                                    '-',
+                                    '-',
+                                    '-',
+                                    mm_masks[wi],
+                                    user_masks[wi],
+                                    ))
+
                 # only log table entry for first field of MM port
                 user_def_name = '-'
                 number_of_peripherals = '-'
@@ -179,20 +272,24 @@ def gen_fpga_map(fpga, fpga_name):
     _map_info = []
     _map_info.append('fpga_name = {}'.format(fpga_name))  # identifies <fpga_name>.fpga.yaml
     _map_info.append('number_of_columns = {}'.format(map_nof_columns))  # to support mmap with more columns in future
-    _map_info.append('# col 1: mm_port_name  # = user defined QSYS name,  if - then it is part of previous MM port.')
+    _map_info.append('# There can be multiple lines with a single key. The host should ignore unknown keys.')
+    _map_info.append('# The lines with columns follow after the number_of_columns keys. The host should ignore')
+    _map_info.append('# the extra columns in case the mmap contains more columns than the host expects.')
+    _map_info.append('#')
+    _map_info.append('# col 1: mm_port_name, if - then it is part of previous MM port.')
     _map_info.append('# col 2: number of peripherals, if - then it is part of previous peripheral.')
     _map_info.append('# col 3: number of mm_ports, if - then it is part of previous MM port.')
     _map_info.append('# col 4: mm_port_type, if - then it is part of previous MM port.')
     _map_info.append('# col 5: field_name')
-    _map_info.append('# col 6: field start address (in words)')
-    _map_info.append('# col 7: field address span (in words)')
-    _map_info.append('# col 8: field access_mode')
-    _map_info.append('# col 9: field radix')
-    _map_info.append('# col 10: radix packing (number of radix elements per word)')
-    _map_info.append('# col 11: radix bit mask (of first radix element in word)')
+    _map_info.append('# col 6: field start address (in MM words)')
+    _map_info.append('# col 7: number of fields, if - then it is part of previous field_name.')
+    _map_info.append('# col 8: field access_mode, if - then it is part of previous field_name.')
+    _map_info.append('# col 9: field radix, if - then it is part of previous field_name.')
+    _map_info.append('# col 10: field mm_mask')
+    _map_info.append('# col 11: field user_mask, if - then it is same as mm_mask')
     _map_info.append('#')
-    _map_info.append('# col1                      col2  col3  col4   col5                                      col6        col7    col8   col9         col10  col11')
-    _map_info.append('# ------------------------  ----  ----  -----  ----------------------------------------  ----------  ------  -----  -----------  -----  ----------')
+    _map_info.append('# col1                      col2  col3  col4   col5                                      col6        col7    col8   col9         col10       col11')
+    _map_info.append('# ------------------------  ----  ----  -----  ----------------------------------------  ----------  ------  -----  -----------  ----------  ----------')
 
     out_dir = os.path.join(os.getenv('ARGS_BUILD_DIR'), fpga.board_name.replace('uniboard','unb'), 'args', fpga_name, 'c')
     try:
diff --git a/gen_slave.py b/gen_slave.py
index c3f746b776a8f1aa38abfb27f634a50efa6b48a1..2683422d0a7a9a3cc1907e1a46ebb8a9f04b9522 100755
--- a/gen_slave.py
+++ b/gen_slave.py
@@ -43,7 +43,7 @@ logger = logging.getLogger('main.gen_mm_port')
 
 
 def word_wise(byte_address):
-    return max(1, int(byte_address / WORD_SIZE))
+    return max(1, int(byte_address / MM_BUS_SIZE))
 
 
 def tab_aligned(input_strings):
@@ -120,7 +120,7 @@ class MMPort(object):
         for mmport in self.mm_ports:
             if not isinstance(mmport, Register) or (isinstance(mmport, Register) and getattr(mmport, 'isIP', False)):
                 continue
-            self.nof_dat = int(mmport.base_address()/WORD_SIZE + mmport.address_length()/WORD_SIZE*mmport.number_of_mm_ports())
+            self.nof_dat = int(mmport.base_address()/MM_BUS_SIZE + mmport.address_length()/MM_BUS_SIZE*mmport.number_of_mm_ports())
 
         if self.nof_dat > 1:
             self.dat_w = DEFAULT_WIDTH  # parameterize
@@ -212,10 +212,10 @@ class MMPort(object):
                         for field in mmport.fields:
                             if field.access_mode() == 'RW':
                                 sublines.append('\t\t' + mmport.name().upper() + '_FIELDS_RW.' + field.name() + '<tabs>=>\n')
-                                # sublines.extend(' ,\t\t -- '+ 'STD_LOGIC' + vector_len(field.width()) +'\n')
+                                # sublines.extend(' ,\t\t -- '+ 'STD_LOGIC' + vector_len(field.mm_width()) +'\n')
                             if field.access_mode() == 'RO':
                                 sublines.append('\t\t' + mmport.name().upper() + '_FIELDS_RO.' + field.name() + '<tabs>=>\n')
-                                # sublines.extend(' ,\t\t -- '+ 'STD_LOGIC' + vector_len(field.width()) +'\n')
+                                # sublines.extend(' ,\t\t -- '+ 'STD_LOGIC' + vector_len(field.mm_width()) +'\n')
                         sublines.extend(self.ram_records(mmport, False))
                     # lines.extend(tab_aligned(sublines))
                     lines.extend(sublines)
@@ -242,12 +242,12 @@ class MMPort(object):
                 regGroup = mmport
                 # regGroup = self.registers[key]
                 fields_dict = regGroup.fields
-                replace_dicts.update({regGroup.name(): {'<name>': self.periph_name, '<reg_ram>': 'reg', '<reg_name>': regGroup.name(), '<adr_w>': 'c_addr_w', '<dat_w>': 'c_dat_w','<nof_dat>' : int(regGroup.address_length()/WORD_SIZE),
-                '<c_init_reg>': self.set_init_string(regGroup)[0], '<nof_mm_ports>' : regGroup.number_of_mm_ports(), '<addr_base>' : int(regGroup.base_address() / WORD_SIZE),
+                replace_dicts.update({regGroup.name(): {'<name>': self.periph_name, '<reg_ram>': 'reg', '<reg_name>': regGroup.name(), '<adr_w>': 'c_addr_w', '<dat_w>': 'c_dat_w','<nof_dat>' : int(regGroup.address_length()/MM_BUS_SIZE),
+                '<c_init_reg>': self.set_init_string(regGroup)[0], '<nof_mm_ports>' : regGroup.number_of_mm_ports(), '<addr_base>' : int(regGroup.base_address() / MM_BUS_SIZE),
                 '<c_clr_mask>': self.set_clr_mask(regGroup)[0]}})
         else :
             self.replaceDict = {'<name>': mmPortSettings.name(), '<lib>': self.prefix, '<adr_w>': ceil_log2(mmPortSettings.address_length()),
-            '<dat_w>': mmPortSettings.width(), '<nof_dat>': mmPortSettings.address_length(), '<nof_dat_by_mm_ports>': mmPortSettings.address_length()*ceil_pow2(mmPortSettings.number_of_mm_ports()), '<init_sl>':str(0),#mmPortSettings.default() if '.coe' not in str(mmPortSettings.default()) else str(0),
+            '<dat_w>': mmPortSettings.mm_width(), '<nof_dat>': mmPortSettings.address_length(), '<nof_dat_by_mm_ports>': mmPortSettings.address_length()*ceil_pow2(mmPortSettings.number_of_mm_ports()), '<init_sl>':str(0),#mmPortSettings.default() if '.coe' not in str(mmPortSettings.default()) else str(0),
             '<RO>' : mmPortSettings.access_mode() == 'RO', '<WO>' : mmPortSettings.access_mode() == 'WO', '<FTHRESHOLD>' : mmPortSettings.address_length()-5, '<ETHRESHOLD>' : 2 , '<nof_mm_ports>':mmPortSettings.number_of_mm_ports(),
             '<t_we_arr>': 't_we_arr' if mmPortSettings.number_of_mm_ports() > 1 else 'std_logic_vector', '<web_range>': 'c_ram_b.nof_mm_ports' if mmPortSettings.number_of_mm_ports() > 1 else 'c_ram_b.dat_w/8'}
             if mm_port_type == 'ram':
@@ -359,7 +359,7 @@ class MMPort(object):
         lines = []
         tmplFile = os.path.join(self.tmpl_dir, "template_{}_axi4.tcl".format(mm_port_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 }
+                    # '<dat_w>' : settings.mm_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
         if 'coe' not in str(default):
             removeDict = {'<coe_file>': '', 'Load_Init_File' : '', '<default>': str(hex(default)).replace('0x', '')}
@@ -405,7 +405,7 @@ class MMPort(object):
             if field.reset_value() != 0:
                 bInit = 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] = list(format(field.reset_value(), '0' + str(field.width()) + 'b'))
+                temp_vector[word_wise(field.address_offset())*self.dat_w+field.bit_offset() + field.mm_width() -1 :lowestBit:-1] = list(format(field.reset_value(), '0' + str(field.mm_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):
@@ -425,7 +425,7 @@ class MMPort(object):
             if "CLR" in field.side_effect():
                 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_vector[word_wise(field.address_offset())*self.dat_w+field.bit_offset() + field.mm_width()-1 :lowestBit:-1] = [1]*field.mm_width()  # list(format(field.default(), '0' + str(field.mm_width()) + 'b'))
         
         temp_string = ''.join(map(str, temp_vector[::-1]))
         hexValue = hex(int(temp_string, 2))[2:]
@@ -466,11 +466,11 @@ class MMPort(object):
         reg_name = regGroup.name()
         for ram in regGroup.rams:
             for i in range(regGroup.number_of_mm_ports()):
-                lines.append('\tCONSTANT c_' + reg_name + '_' + ram.name() + (str(i) if regGroup.number_of_mm_ports() > 1 else '') + '_address<tabs>: t_register_address := ('+ 'base_address => ' + str(word_wise(ram.base_address()) + (i)*(ceil_pow2(ram.number_of_fields()))) +', address => 0, offset => 0, width => ' + str(ram.width()) + ', name => pad("' + reg_name + '_' + ram.name() + '"));\n' )
+                lines.append('\tCONSTANT c_' + reg_name + '_' + ram.name() + (str(i) if regGroup.number_of_mm_ports() > 1 else '') + '_address<tabs>: t_register_address := ('+ 'base_address => ' + str(word_wise(ram.base_address()) + (i)*(ceil_pow2(ram.number_of_fields()))) +', address => 0, offset => 0, width => ' + str(ram.mm_width()) + ', name => pad("' + reg_name + '_' + ram.name() + '"));\n' )
         for i in range(regGroup.number_of_mm_ports()):
             mm_port_index = '_' + str(i) if regGroup.number_of_mm_ports() > 1 else ''
             for field in regGroup.fields:
-                lines.append('\tCONSTANT c_' + reg_name + '_' + field.name() + '_address'+mm_port_index+'<tabs>: t_register_address := (' + 'base_address => ' + str(word_wise(regGroup.base_address())+i*word_wise(regGroup.address_length())) +', address => ' + str(word_wise(field.address_offset())) + ', offset => ' + str(field.bit_offset()) + ', width => ' + str(field.width()) + ', name => pad("' + reg_name + '_' + field.name() + '"));\n' )
+                lines.append('\tCONSTANT c_' + reg_name + '_' + field.name() + '_address'+mm_port_index+'<tabs>: t_register_address := (' + 'base_address => ' + str(word_wise(regGroup.base_address())+i*word_wise(regGroup.address_length())) +', address => ' + str(word_wise(field.address_offset())) + ', offset => ' + str(field.bit_offset()) + ', width => ' + str(field.mm_width()) + ', name => pad("' + reg_name + '_' + field.name() + '"));\n' )
         if regGroup.number_of_mm_ports() > 1 :
             for field in regGroup.fields:
                 fields_string = ""
@@ -481,7 +481,7 @@ class MMPort(object):
             # lowerBit = self.dat_w*field.address_offset() + field.bit_offset()
 
             # lines.append('\tCONSTANT c_lwr_' + reg_name + '_' + field.name() + '\t' + aligned_tabs(field.name(), 32)*'\t' + 'NATURAL := ' + str(lowerBit) + ';\n'  )
-            # upper_bit = self.dat_w*field.address_offset() + field.bit_offset() + field.width() - 1
+            # upper_bit = self.dat_w*field.address_offset() + field.bit_offset() + field.mm_width() - 1
             # lines.append('\tCONSTANT c_upr_' + reg_name + '_' + field.name() + '\t' + aligned_tabs(field.name(), 32)*'\t' + 'NATURAL := ' + str(upper_bit) + ';\n'  )
         return(lines)
 
@@ -492,7 +492,7 @@ class MMPort(object):
             mm_port_ram_name = regGroup.name() + '_' + ram.name()
             bit_type = 'STD_LOGIC' if nof_inst == 1 else ('STD_LOGIC_VECTOR(0 to ' + str(nof_inst-1) + ')')
             adr_type = ('STD_LOGIC_VECTOR('+str(ceil_log2(ram.number_of_fields())-1)+' downto 0)' if nof_inst == 1 else ('t_slv_<>_arr(0 to ' + str(nof_inst-1) + ')')).replace('<>', str(ceil_log2(ram.number_of_fields())))
-            rd_type = 'STD_LOGIC_VECTOR('+str(ram.width()-1)+' downto 0)' if nof_inst == 1 else ('t_slv_'+str(ram.width())+'_arr(0 to ' + str(nof_inst-1) + ')')
+            rd_type = 'STD_LOGIC_VECTOR('+str(ram.mm_width()-1)+' downto 0)' if nof_inst == 1 else ('t_slv_'+str(ram.mm_width())+'_arr(0 to ' + str(nof_inst-1) + ')')
             # write user_out record
             in_ports = {'adr' : adr_type, 'wr_dat' : rd_type, 'wr_en' : bit_type, 'rd_en' : bit_type, 'clk' : bit_type, 'rst': bit_type}
             out_ports = {'rd_dat' : rd_type, 'rd_val': bit_type}
@@ -527,18 +527,18 @@ class MMPort(object):
             if nof_inst != 1:
                 for field in field_list:
                     if field.access_mode() == mode:
-                        if field.width() == 1:
+                        if field.mm_width() == 1:
                             data_type = 'std_logic_vector(0 to ' + str(nof_inst - 1)
                         else :
-                            data_type = 't_slv_' + str(field.width()) + '_arr(0 to ' + str(nof_inst - 1)
+                            data_type = 't_slv_' + str(field.mm_width()) + '_arr(0 to ' + str(nof_inst - 1)
                         lines.append('\t\t' + field.name().lower() + '<tabs>' + ': ' + data_type + ');\n')
             else:  # no arrays required
                 for field in field_list:
                     if field.access_mode() == mode:
-                        if field.width() == 1:
+                        if field.mm_width() == 1:
                             data_type = 'std_logic'
                         else :
-                            data_type = 'std_logic_vector('+str(field.width()-1)+' downto 0)'
+                            data_type = 'std_logic_vector('+str(field.mm_width()-1)+' downto 0)'
                         lines.append('\t\t' + field.name().lower() + '<tabs>' + ': ' + data_type + ';\n')
             lines.append('\tEND RECORD;\n\n')
 
@@ -585,7 +585,7 @@ class MMPort(object):
         wr_val_vec_dict = {'postfix' : 'wr_val_vec', 'type' : ': STD_LOGIC_VECTOR(0 downto 0);\n'}
         adr_dict = {'postfix' : 'adr', 'type' : ': STD_LOGIC_VECTOR(<>.adr_w-1 downto 0);\n'}
         rd_en_dict = {'postfix' : 'rd_en', 'type' : ': STD_LOGIC_VECTOR(<>.nof_mm_ports-1 downto 0);\n' if nof_mm_ports > 1 else ': STD_LOGIC;\n'}
-        rd_dat_dict = {'postfix' : 'rd_dat', 'type' : ': t_slv_'+str(regGroup.width())+'_arr(0 to <>.nof_mm_ports-1);\n' if nof_mm_ports > 1 else ': STD_LOGIC_VECTOR(<>.dat_w-1 downto 0);\n'}
+        rd_dat_dict = {'postfix' : 'rd_dat', 'type' : ': t_slv_'+str(regGroup.mm_width())+'_arr(0 to <>.nof_mm_ports-1);\n' if nof_mm_ports > 1 else ': STD_LOGIC_VECTOR(<>.dat_w-1 downto 0);\n'}
         rd_val_dict = {'postfix' : 'rd_val', 'type' : ': STD_LOGIC_VECTOR(<>.nof_mm_ports-1 downto 0);\n' if nof_mm_ports > 1 else ': STD_LOGIC;\n'}
 
         if ram :
@@ -618,14 +618,14 @@ class MMPort(object):
             index = ''
 
         for field in field_list:
-            upper_bit = field.bit_offset() + field.width() - 1
+            upper_bit = field.bit_offset() + field.mm_width() - 1
             if field.side_effect() != 'PW':  # and field.side_effect() != 'PR':
-                mapped_field = regGroup.name().upper() + '_FIELDS_RO.' + field.name() + index if field.access_mode() == 'RO' else '<reg_name>_out_reg('+mm_port_offset+'c_byte_w*' + str(field.address_offset()) + ('+' + str(upper_bit) if upper_bit is not 0 else '') + ((' downto '+mm_port_offset+'c_byte_w*'+ str(field.address_offset()) + ('+' + str(field.bit_offset()) if field.bit_offset() is not 0 else '')) if field.width() > 1 else '') + ')'   # doesn't test for other cases WO etc.
+                mapped_field = regGroup.name().upper() + '_FIELDS_RO.' + field.name() + index if field.access_mode() == 'RO' else '<reg_name>_out_reg('+mm_port_offset+'c_byte_w*' + str(field.address_offset()) + ('+' + str(upper_bit) if upper_bit is not 0 else '') + ((' downto '+mm_port_offset+'c_byte_w*'+ str(field.address_offset()) + ('+' + str(field.bit_offset()) if field.bit_offset() is not 0 else '')) if field.mm_width() > 1 else '') + ')'   # doesn't test for other cases WO etc.
             else :
-                mapped_field = '\'' + '0'*field.width() + '\''
-                if field.width() > 1 :
+                mapped_field = '\'' + '0'*field.mm_width() + '\''
+                if field.mm_width() > 1 :
                     mapped_field = mapped_field.replace('\'', '\"')
-            if field.width() > 1:
+            if field.mm_width() > 1:
                 line = '\t'*tab_no + '<reg_name>_in_reg('+mm_port_offset+'c_byte_w*' + str(field.address_offset()) + ('+' + str(upper_bit) if upper_bit is not 0 else '' ) +  ' downto '+mm_port_offset+'c_byte_w*'+ str(field.address_offset()) + ('+' + str(field.bit_offset()) if field.bit_offset() is not 0 else '') + ')<tabs><= ' + mapped_field +  ';\n'
                 lines.append(line)
                 # lines.append('\t\t' + regGroup.name() + '_in_reg( + c_mm_' + regGroup.name() + '_reg.dat_w*' + str(field.address_offset()) +  ('+' + str(upper_bit) if upper_bit is not 0 else '' )  + ' downto c_mm_reg.dat_w*'+ str(field.address_offset()) + ('+' + str(field.bit_offset()) if field.bit_offset() is not 0 else '') + ') <= ' + mapped_field +  ';\n')
@@ -654,17 +654,17 @@ class MMPort(object):
             index = ''
         for field in field_list:
             if field.access_mode() == 'RW':
-                upper_bit = field.bit_offset() + field.width() - 1
+                upper_bit = field.bit_offset() + field.mm_width() - 1
                 side_effect = ''
                 # multiple side effects need to be supported
                 if 'P' in field.side_effect():
                     side_effect_string = '<reg_name>_pulse_<>(' + mm_port_offset_pulse + str(word_wise(field.address_offset()))
-                    side_effect_string = (side_effect_string + ')') if field.width() == 1 else '(' + str(field.width()-1) + ' downto 0 => ' + side_effect_string + '))'
+                    side_effect_string = (side_effect_string + ')') if field.mm_width() == 1 else '(' + str(field.mm_width()-1) + ' downto 0 => ' + side_effect_string + '))'
                     if 'PW' in field.side_effect() and 'PR' in field.side_effect():
                         side_effect = '(' + side_effect_string.replace('<>', 'write') + ' OR ' + (side_effect_string.replace('<>', 'read')) + ') AND '
                     else :
                         side_effect = side_effect_string.replace('<>', 'write' if 'PW' in field.side_effect() else 'read') + ' AND '
-                if field.width() > 1:
+                if field.mm_width() > 1:
                     lines.append(tab_no*'\t' + regGroup.name().upper()+'_FIELDS_RW.' + field.name() + index+'<tabs><= '+side_effect+'<reg_name>_out_reg(' + mm_port_offset + 'c_byte_w*' + str(field.address_offset()) + ('+' + str(upper_bit) if upper_bit is not 0 else '') + ' downto '+ mm_port_offset +'c_byte_w*'+ str(field.address_offset()) +(  '+' +   str(field.bit_offset()) if field.bit_offset() is not 0 else '') + ');\n')
                 else :
                     lines.append(tab_no*'\t' + regGroup.name().upper()+'_FIELDS_RW.' + field.name() + index+'<tabs><= '+side_effect+'<reg_name>_out_reg(' + mm_port_offset + 'c_byte_w*' + str(field.address_offset()) + ('+' + str(upper_bit) if upper_bit is not 0 else '') + ');\n')
@@ -723,7 +723,7 @@ class MMPort(object):
             sublines = []
             with open(tmpl_file, 'r') as inst_file:
                 for subline in inst_file:
-                    for tag, replace_string in {'<reg_name>' : self.regGroup.name() + '_' + ram.name(), '<reg_ram>': 'ram', '<adr_w>' : str(ceil_log2(ram.number_of_fields())), '<nof_dat>' : str(ram.number_of_fields()), '<nof_mm_ports>' : self.regGroup.number_of_mm_ports(), '<addr_base>' : str(int(ram.base_address()/WORD_SIZE)), '<dat_w>' : ram.width()}.items():
+                    for tag, replace_string in {'<reg_name>' : self.regGroup.name() + '_' + ram.name(), '<reg_ram>': 'ram', '<adr_w>' : str(ceil_log2(ram.number_of_fields())), '<nof_dat>' : str(ram.number_of_fields()), '<nof_mm_ports>' : self.regGroup.number_of_mm_ports(), '<addr_base>' : str(int(ram.base_address()/MM_BUS_SIZE)), '<dat_w>' : ram.mm_width()}.items():
                         subline = subline.replace(tag, str(replace_string))
                     if 'STD_LOGIC_VECTOR' not in subline:
                         sublines.append(subline)
@@ -757,7 +757,7 @@ class MMPort(object):
     def read_data_compare(self, regGroup, i, nof_regs):
         lines = []
         for ram in regGroup.rams:
-            partial = 32 - ram.width()
+            partial = 32 - ram.mm_width()
             others = '' if partial == 0 else '\'0\' & ' if partial == 1 else '\"'+'0'*partial+'\" & '
             for j in range(regGroup.number_of_mm_ports()):
                 lines.append('\t'*2 + others + regGroup.name() + '_' + ram.name() + '_rd_dat' + ('('+str(j)+')' if regGroup.number_of_mm_ports() > 1 else '') + (';' if (i == regGroup.number_of_mm_ports()-1 and ram == regGroup.rams[-1] and len(regGroup.fields) == 0) else ' WHEN '+regGroup.name() + '_' + ram.name()+'_rd_val<> = \'1\' ELSE\n'.replace('<>', '(' + str(j) + ')' if regGroup.number_of_mm_ports() > 1 else '')))
@@ -765,18 +765,18 @@ class MMPort(object):
         return lines
     # def fix_mem_size(self, mmPortSettings, mm_port_type): # should be in peripheral.py
         # if mmPortSettings.() < 32:
-            # logger.warning('Updating %s width %d to 32 (minimum data width)', mm_port_type, mmPortSettings.width())
-            # mmPortSettings.width(32)
+            # logger.warning('Updating %s width %d to 32 (minimum data width)', mm_port_type, mmPortSettings.mm_width())
+            # mmPortSettings.mm_width(32)
 
         # if mm_port_type.lower() == 'ram':
             # if mmPortSettings.address_length() < 1024:
                 # mmPortSettings.depth(1024)
 
 
-            # if mmPortSettings.width_b() == mmPortSettings.width() :
+            # if mmPortSettings.width_b() == mmPortSettings.mm_width() :
                 # self.depth_b = mmPortSettings.address_length()
             # else :
-                # self.depth_b = mmPortSettings.address_length() / (mmPortSettings.width_b()/mmPortSettings.width())
+                # self.depth_b = mmPortSettings.address_length() / (mmPortSettings.width_b()/mmPortSettings.mm_width())
                 # if self.depth_b < 1024:
                     # logger.warning('BRAM port B violation: BRAM controller requires minimum depth of 1k')
 
diff --git a/py_args_lib/fpga.py b/py_args_lib/fpga.py
index 4c829a6122127bde393d62eeab00729f21e7c4e4..e28dd0604a2b6729dac8b0bdb6bd3d4cfc1634e1 100644
--- a/py_args_lib/fpga.py
+++ b/py_args_lib/fpga.py
@@ -312,13 +312,13 @@ class FPGA(object):
 
                     addr_map_name = [peripheral.name(), mmport.name()]
 
-                    if isinstance(mmport, Register) and not getattr(mmport, 'isIP', False):
+                    if isinstance(mmport, Register):
                         addr_map_name.append('reg')
 
                         ram_span = int(mmport.base_address() - mmport.rams[0].base_address()) if any(mmport.rams) else 0
                         mm_port_span = int(mmport.address_length() * mmport.number_of_mm_ports() * max(ram_span, 1))
                         
-                        logger.debug('REG-not-IP mmport.address_len=%d * mmport.number_of_mm_ports=%d * ram_span=%d', mmport.address_length(), mmport.number_of_mm_ports(), ram_span)
+                        logger.debug('REG mmport.address_len=%d * mmport.number_of_mm_ports=%d * ram_span=%d', mmport.address_length(), mmport.number_of_mm_ports(), ram_span)
                         logger.debug("REG MM port %s_%s has span 0x%x", peripheral.name(), mmport.name() , mm_port_span)
 
                         if peripheral.number_of_peripherals() > 1:
@@ -330,20 +330,6 @@ class FPGA(object):
                                                           'periph_num': periph_num, 'mm_port': mmport}
                         logger.debug("Register for %s has span 0x%x", peripheral.name(), mm_port_span)
 
-                    elif isinstance(mmport, Register) and getattr(mmport, 'isIP', False):
-                        addr_map_name.append('regip')
-
-                        mm_port_span = int(ceil_pow2(max(mmport.address_length() * mmport.number_of_mm_ports(), 4096)))  # mmport.address_length()*mmport.number_of_mm_ports()#
-
-                        logger.debug('REG-IP mmport.address_len=%d * mmport.number_of_mm_ports=%d', mmport.address_length(), mmport.number_of_mm_ports())
-                        logger.debug("REG-IP MM port %s_%s has span 0x%x", peripheral.name(), mmport.name() , mm_port_span)
-                        
-                        mm_port_name = '_'.join(addr_map_name)
-
-                        self.address_map[mm_port_name] = {'base': base_addr, 'span': mm_port_span, 'type': mmport.protocol,
-                                                          'port_index': eval("self.nof_{}".format(mmport.protocol.lower())),
-                                                          'peripheral': peripheral, 'periph_num': periph_num, 'mm_port': mmport}
-
                     elif isinstance(mmport, RAM):
                         addr_map_name.append('ram')
 
@@ -407,7 +393,7 @@ class FPGA(object):
             for periph_num in range(peripheral.number_of_peripherals()):
                 assigned_reg = False
                 for mmport in peripheral.mm_ports:
-                    if isinstance(mmport, Register) and not getattr(mmport, 'isIP', False):
+                    if isinstance(mmport, Register):
                         mm_port_type = 'reg'
                         if assigned_reg is False:  # calc for entire register MM port
                             reg_span = ceil_pow2(max(peripheral.reg_len, 4096))
@@ -432,23 +418,6 @@ class FPGA(object):
                         self.nof_lite = self.nof_lite + 1
                         assigned_reg = True
 
-                    elif isinstance(mmport, Register) and getattr(mmport, 'isIP', False):
-                        mm_port_type = 'regip'
-                        mm_port_span = ceil_pow2(max(mmport.address_length() * mmport.number_of_mm_ports(), 4096))  # mmport.address_length()*mmport.number_of_mm_ports()#
-                        lowest_free_addr = int(np.ceil(lowest_free_addr / mm_port_span) * mm_port_span)
-
-                        _name_list = [peripheral.name(), mmport.name(), mm_port_type]
-                        mm_port_name = '_'.join(_name_list)
-
-                        self.address_map[mm_port_name] = {'base': lowest_free_addr, 'span': mm_port_span, 'type': mmport.protocol,
-                                                          'port_index': eval("self.nof_{}".format(mmport.protocol.lower())),
-                                                          'peripheral': peripheral, 'periph_num': periph_num, 'mm_port': mmport}
-                        if mmport.protocol.lower() == 'lite':
-                            self.nof_lite = self.nof_lite + 1
-                        else :
-                            self.nof_full = self.nof_full + 1
-                        lowest_free_addr = lowest_free_addr + mm_port_span
-
                     elif isinstance(mmport, RAM):
                         mm_port_type = 'ram'
                         size_in_bytes = mmport.address_length() * ceil_pow2(mmport.number_of_mm_ports())
@@ -486,9 +455,6 @@ class FPGA(object):
                             self.nof_full = self.nof_full + 1
                             lowest_free_addr = lowest_free_addr + mm_port_span
 
-                    if mm_port_span > 65536:
-                        logger.error("MM port %s has mm_port_span %d. Maximum mm_port_span in Vivado Address Editor is 64kB", mmport.name(), mm_port_span)
-                        sys.exit()
 
     def show_overview(self):
         """ print FPGA overview
diff --git a/py_args_lib/peripheral.py b/py_args_lib/peripheral.py
index 07fb45f5773930750978f002fd4d505ee5af818a..a0600b94d035992cc5dfc098093978fecd82f326 100644
--- a/py_args_lib/peripheral.py
+++ b/py_args_lib/peripheral.py
@@ -35,9 +35,12 @@ import logging
 import yaml
 import collections
 from peripheral_lib import *
+from common_func import ceil_div
 
 logger = logging.getLogger('main.peripheral')
 
+# EK TODO better put class Peripheral and class PeripheralLibrary in separate files?
+# EK TODO better put class PeripheralLibrary and class FPGALibrary in same file? --> No
 
 class Peripheral(BaseObject):
     """ A Peripheral consists of one or more MM ports. The MM port can be a
@@ -194,8 +197,8 @@ class Peripheral(BaseObject):
                     sys.exit()
 
                 i = 0
-                if mm_port_info.get('mm_port_type', '').upper() in VALID_SLAVE_TYPES:
-                    number_of_mm_ports = mm_port_info.get('number_of_mm_ports', DEFAULT_NUMBER_OF_SLAVES)
+                if mm_port_info.get('mm_port_type', '').upper() in VALID_MM_PORT_TYPES:
+                    number_of_mm_ports = mm_port_info.get('number_of_mm_ports', DEFAULT_NUMBER_OF_MM_PORTS)
 
                     fields = []
                     if 'fields' in mm_port_info:
@@ -270,8 +273,7 @@ class Peripheral(BaseObject):
                             self.add_fifo(mm_port_nr, mm_port_name, field, number_of_mm_ports)
                     else:  # mm_port_type is REG or REG_IP
                         logger.debug('adding register %s\n', mm_port_name)
-                        self.add_register(mm_port_nr, mm_port_name, fields, number_of_mm_ports, mm_port_info['mm_port_type'].upper() == 'REG_IP',
-                                          mm_port_info.get('mm_port_protocol', None), mm_port_info.get('mm_port_span', None))
+                        self.add_register(mm_port_nr, mm_port_name, fields, number_of_mm_ports, mm_port_info.get('mm_port_span', None))
                     if 'mm_port_description' in mm_port_info.keys():
                         self.mm_ports[-1].update_args({'mm_port_description': mm_port_info['mm_port_description']})
                     if 'dual_clock' in mm_port_info.keys():
@@ -333,16 +335,13 @@ class Peripheral(BaseObject):
         """
         self._parameters[name] = value
 
-    def add_register(self, mm_port_nr, name, fields, number_of_mm_ports, isIP, protocol, mm_port_span):
+    def add_register(self, mm_port_nr, name, fields, number_of_mm_ports, mm_port_span):
         """ add register to peripheral
         """
 
         register = deepcopy(self.init_mm_port('Register', name, fields))
         register.number_of_mm_ports(number_of_mm_ports)
-        register.isIP = isIP
         register.mm_port_span = mm_port_span
-        if protocol is not None and protocol.upper() in ['LITE', 'FULL']:   # EK: TODO Remove AXI specific protocol field
-            register.protocol = protocol.upper()
         self.registers['mm_port_{}'.format(mm_port_nr)] = register
         self.mm_ports.append(register)
 
@@ -387,14 +386,16 @@ class Peripheral(BaseObject):
                 sys.exit()
             if field.group_name() is not None:
                 field.name(field.group_name() + '_' + field.name())
-            field.width(val=self._eval(field.width(), int))
+            field.mm_width(val=self._eval(field.mm_width(), int))
+            if field.user_width() == None:
+                field.user_width(field.mm_width())  # EK: TODO why can self._eval(field.user_width(), int)) not work for None ?
+            field.user_width(val=self._eval(field.user_width(), int))   # EK TODO why is there no _eval for every field key?
             field.bit_offset(val=self._eval(field.bit_offset(), int))
             field.access_mode(val=self._eval(field.access_mode()))
             field.side_effect(val=self._eval(field.side_effect()))
             field.address_offset(val=self._eval(field.address_offset(), int))
             field.number_of_fields(val=self._eval(field.number_of_fields(), int))
             field.reset_value(val=self._eval(field.reset_value(), int))
-            field.software_value(val=self._eval(field.software_value(), int))
             field.radix(val=self._eval(field.radix()))
 
     def eval_fifo(self):
@@ -409,9 +410,9 @@ class Peripheral(BaseObject):
             fifo = mmport
             fifo.number_of_mm_ports(val=self._eval(fifo.number_of_mm_ports()))
             logger.debug("  -FIFO depth (fields): %s", fifo.number_of_fields())
-            fifo.address_length(val=ceil_pow2(self._eval(fifo.number_of_fields())) * WORD_SIZE)  # address_length in bytes
+            fifo.address_length(val=ceil_pow2(self._eval(fifo.number_of_fields())) * MM_BUS_SIZE)  # address_length in bytes
             logger.debug("  -FIFO depth (bytes): %s", fifo.address_length())
-            logger.debug("  -FIFO width (bits): %s", fifo.width())
+            logger.debug("  -FIFO width (bits): %s", fifo.mm_width())
 
     def eval_ram(self):
         """Evaluate the parameters and the nof_inst of the peripheral in order to define the
@@ -427,10 +428,10 @@ class Peripheral(BaseObject):
                 continue
 
             self.eval_fields([ram])
-            if ram.width() < DEFAULT_FIELD_WIDTH:
-                ram.width(DEFAULT_FIELD_WIDTH)
-            if ram.user_width() < DEFAULT_FIELD_WIDTH:
-                ram.user_width(DEFAULT_FIELD_WIDTH)
+            #if ram.mm_width() < DEFAULT_FIELD_MM_WIDTH:                # EK: TODO remove these lines --> better generalize REG, RAM and FIFO
+            #    ram.mm_width(DEFAULT_FIELD_MM_WIDTH)
+            #if ram.user_mm_width() < DEFAULT_FIELD_MM_WIDTH:
+            #    ram.user_mm_width(DEFAULT_FIELD_MM_WIDTH)
 
             ram.number_of_mm_ports(val=self._eval(ram.number_of_mm_ports()))
 
@@ -443,18 +444,24 @@ class Peripheral(BaseObject):
             logger.debug("  -RAM depth (fields): %s", ram.number_of_fields())
             # Here the variables are used to evaluate the true value for the depth
             # parameter(taking int account the nof_inst as well)
-            ram.address_length(val=ceil_pow2(self._eval(ram.number_of_fields())) * WORD_SIZE)  # address_length in bytes
+
+            # EK: TODO account for that number_of_fields() now counts fields instead of MM words, anyways the address length calculation should not be done here in the ARGS parser
+            #number_of_fields_per_word = 1
+            #if ram.user_width() != None:
+            #    number_of_fields_per_word = ceil_div(ram.mm_width(), ram.user_width())
+
+            ram.address_length(val=ceil_pow2(self._eval(ram.number_of_fields())) * MM_BUS_SIZE)  # address_length in bytes
             logger.debug("  -RAM depth (bytes): %d", ram.address_length())
 
-            logger.debug("  -RAM width (bits): %s", ram.width())
+            logger.debug("  -RAM width (bits): %s", ram.mm_width())
             # Here the variables are used to evaluate the true value for the width parameter.
             ram.user_depth(val=self._eval(ram.user_depth()))
-            ram.user_width(val=self._eval(ram.user_width()))
+            #ram.user_mm_width(val=self._eval(ram.user_width()))                    # EK: TODO remove these lines --> better generalize REG, RAM and FIFO
             ram.update_address_length()
-            logger.debug("  -RAM width eval: %d (bits)", ram.width())
+            logger.debug("  -RAM width eval: %d (bits)", ram.mm_width())
             logger.debug("  %s access_mode: %s", ram.name(), ram.access_mode())
             logger.debug("  %s depth: %d (bytes)", ram.name(), ram.address_length())
-            logger.debug("  %s width: %d (bits)", ram.name(), ram.user_width())
+            #logger.debug("  %s width: %d (bits)", ram.name(), ram.user_width())         # EK: TODO remove these lines --> better generalize REG, RAM and FIFO
 
     def eval_register(self):
         """Evaluate the register address_length based on the evaluation of the fields,
@@ -480,12 +487,12 @@ class Peripheral(BaseObject):
                         if i > 0:
                             if _field.bit_offset() != UNASSIGNED_BIT and _field.group_name() is not None:  # if fields repeated within the same group and bit offset is set, reset to 0 from the 2nd repeated field
                                 # TODO-PD check size, max = 32 bit
-                                _field.bit_offset(field.bit_offset() + i * _field.width())  # duplicate within the field group
+                                _field.bit_offset(field.bit_offset() + i * _field.mm_width())  # duplicate within the field group
 
                             if _field.address_offset() != UNASSIGNED_ADDRESS:  # field is manually assigned
                                 # TODO-PD check, only bit size check is done if address offset is set?
-                                if _field.group_name() is None or (field.bit_offset() + _field.number_of_fields() * _field.width()) >= DEFAULT_FIELD_WIDTH:  # not part of a field group
-                                    _field.address_offset(field.address_offset() + i * WORD_SIZE)  # duplicate across addresses
+                                if _field.group_name() is None or (field.bit_offset() + _field.number_of_fields() * _field.mm_width()) >= DEFAULT_FIELD_MM_WIDTH:  # not part of a field group
+                                    _field.address_offset(field.address_offset() + i * MM_BUS_SIZE)  # duplicate across addresses
 
                         fields_eval.append(_field)
                 else:
@@ -504,8 +511,8 @@ class Peripheral(BaseObject):
             # set base addresses for reg fields implemented as RAM
             base_addr = 0
             for field in register.rams:
-                base_addr = ceil(base_addr / (ceil_pow2(field.number_of_fields()) * WORD_SIZE)) * ceil_pow2(field.number_of_fields()) * WORD_SIZE  # lowest possible base_addr
-                base_addr = base_addr + ceil_pow2(field.number_of_fields()) * WORD_SIZE * register.number_of_mm_ports()  # new base address
+                base_addr = ceil(base_addr / (ceil_pow2(field.number_of_fields()) * MM_BUS_SIZE)) * ceil_pow2(field.number_of_fields()) * MM_BUS_SIZE  # lowest possible base_addr
+                base_addr = base_addr + ceil_pow2(field.number_of_fields()) * MM_BUS_SIZE * register.number_of_mm_ports()  # new base address
                 field.base_address(base_addr)
 
             # ### Assigned Address and bits to register fields
@@ -525,15 +532,15 @@ class Peripheral(BaseObject):
                 if field.group_name() != last_group or field.group_name() is None:
                     if field.group_name() is not None:
                         field_group = [_field for _field in fields_eval if _field.group_name() == field.group_name()]
-                        occupied_bits = [bit for _field in field_group for bit in range(_field.bit_offset(), _field.bit_offset()+_field.width()) if _field.bit_offset() != UNASSIGNED_BIT]
+                        occupied_bits = [bit for _field in field_group for bit in range(_field.bit_offset(), _field.bit_offset()+_field.mm_width()) if _field.bit_offset() != UNASSIGNED_BIT]
                     else :
-                        occupied_bits = list(range(field.bit_offset(), field.bit_offset() + field.width())) if field.bit_offset() != UNASSIGNED_BIT else []
+                        occupied_bits = list(range(field.bit_offset(), field.bit_offset() + field.mm_width())) if field.bit_offset() != UNASSIGNED_BIT else []
 
                     while (lowest_free_addr) in occupied_addresses:
-                        lowest_free_addr = lowest_free_addr + WORD_SIZE
+                        lowest_free_addr = lowest_free_addr + MM_BUS_SIZE
 
-                    if len(set(occupied_bits)) < len(occupied_bits) or any([bit > (DATA_WIDTH - 1) or bit < 0 for bit in occupied_bits]):
-                        logger.error('*.peripheral.yaml: Manually assigned bits for field %s is outside of data width or contains bit collisions',
+                    if len(set(occupied_bits)) < len(occupied_bits) or any([bit > (MM_BUS_WIDTH - 1) or bit < 0 for bit in occupied_bits]):
+                        logger.error('*.peripheral.yaml: Manually assigned bits for field %s is outside of MM_BUS_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()
@@ -560,15 +567,15 @@ class Peripheral(BaseObject):
 
                 if field.bit_offset() == UNASSIGNED_BIT:
                     free_bit = 0
-                    while any([i in occupied_bits for i in range(free_bit, free_bit + field.width() + 1)]):  # bit is occupied
+                    while any([i in occupied_bits for i in range(free_bit, free_bit + field.mm_width() + 1)]):  # bit is occupied
                         free_bit = free_bit + 1  # try next bit
-                        if free_bit == DEFAULT_FIELD_WIDTH:  # 31 didn't work
+                        if free_bit == DEFAULT_FIELD_MM_WIDTH:  # 31 didn't work
                             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()))
+                occupied_bits = occupied_bits + list(range(field.bit_offset(), field.bit_offset() + field.mm_width()))
 
             # re-sort fields to be ordered by address and bit offsets
             fields_eval.sort(key=lambda x: x.address_offset())
@@ -631,7 +638,7 @@ class Peripheral(BaseObject):
                 field = ram
                 logger.debug("        %-20s:", _name)
                 logger.debug("          width=%-2s       number_of_fields=%s",
-                             str(field.width()), str(field.number_of_fields()))
+                             str(field.mm_width()), str(field.number_of_fields()))
 
         for reg in self.mm_ports:
             if not isinstance(reg, Register):
@@ -646,9 +653,9 @@ class Peripheral(BaseObject):
 
                 logger.debug("        %-20s", field.name())
                 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())
-                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.mm_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",
+                             str(field.bit_offset()), str(field.number_of_fields()), field.side_effect())
 
         logger.debug("  parameters:")
         for param_key, param_val in self._parameters.items():
@@ -764,6 +771,9 @@ class PeripheralLibrary(object):
 
         """
         if peripheral_library is not None:
+            #print('{}'.format(peripheral_library in self.library) )
+            #print('{}'.format(periph_name in self.library[peripheral_library]['peripherals']) )
+            #print('{}'.format(self.library[peripheral_library]) )
             if peripheral_library in self.library and periph_name in self.library[peripheral_library]['peripherals']:
                 logger.debug("Peripheral %s found under library %s", periph_name, peripheral_library)
                 return self.library[peripheral_library]['peripherals'][periph_name]
diff --git a/py_args_lib/peripheral_lib/common_func.py b/py_args_lib/peripheral_lib/common_func.py
index 3c981e6d747327913173d9c5eb267bd8bd5d1f09..ce7d978fa03a62a26231608005d918840147e599 100644
--- a/py_args_lib/peripheral_lib/common_func.py
+++ b/py_args_lib/peripheral_lib/common_func.py
@@ -39,7 +39,26 @@ def ceil_pow2(num):
     """ Return power of 2 value that is equal or greater than num """
     return 2**ceil_log2(num)
 
-    
+
+def ceil_div(num, den):
+    """ Return integer ceil value of num / den """
+    return int(math.ceil( num / float(den) ) )
+
+
+def smallest(a, b):
+    if a<b:
+        return a
+    else:
+        return b
+
+
+def largest(a, b):
+    if a>b:
+        return a
+    else:
+        return b
+
+
 def sel_a_b(sel, a, b):
     """ Return a when sel = True, else return b """
     if sel:
diff --git a/py_args_lib/peripheral_lib/constants.py b/py_args_lib/peripheral_lib/constants.py
index 298dfae436995dad89bf3fd93a27d35e3c092917..d4b561e5ba35d9df3f4af35d6737df66c5952af4 100644
--- a/py_args_lib/peripheral_lib/constants.py
+++ b/py_args_lib/peripheral_lib/constants.py
@@ -32,71 +32,74 @@ c_byte_w      = 8
 c_word_w      = 32
 c_nof_complex = 2
 
-# EK/PD TODO I8, U8, I16, U16, I32, U32, I64, U64, F32, D64, C8, C16, C32 kunnen gebruiken, is dan gelijk radix.
-# I => integer, U => unsigned integer, F => float, D => double en C => complex.
-# En dan 2 functie bits() en bytes() zodat je weet of je bits of bytes gebruikt, bv bits(U16), bytes(U16) ipv HALF_WORD_WIDTH, HALF_WORD_SIZE
-# EK: use separate radix and radix_width.
-
-WORD_WIDTH      = c_word_w                   # = 32 bits
-WORD_SIZE       = c_word_w // c_byte_w       # =  4 bytes
+MM_BUS_WIDTH    = c_word_w                   # = 32 bits
+MM_BUS_SIZE     = c_word_w // c_byte_w       # =  4 bytes
 
 VALID_SCHEMA_NAME  = ['args']
 VALID_SCHEMA_TYPES = ['fpga', 'peripheral']
 
-VALID_SLAVE_TYPES  = ['REG', 'RAM', 'FIFO', 'REG_IP']   # EK: TODO Remove REG_IP
+VALID_MM_PORT_TYPES  = ['REG', 'RAM', 'FIFO', 'REG_IP']   # EK: TODO Remove REG_IP
 
 VALID_FIELD_ACCESS_MODES = ['RO', 'WO', 'RW']   # Read only, Write only, Read/Write. For FIFO use RO = read from FIFO, WO = write to FIFO.
 
 VALID_FIELD_SIDE_EFFECTS = ['CLR', 'PR', 'PW', 'MON']   # EK: TODO Remove MON
 
+VALID_FIELD_USER_WORD_ORDERS = ['le', 'be']
+
+# EK: Use same order of field keys as in ARGS document table (first name, then address/bits related keys, then other property keys)
 VALID_FIELD_KEYS = ['field_description',
-                    'width',
-                    'user_width',
+                    'number_of_fields',
+                    'address_offset',
                     'bit_offset',
+                    'mm_width',
+                    'user_width',
+                    'user_word_order',
+                    'radix',
+                    'resolution_w',
                     'access_mode',
                     'side_effect',
-                    'address_offset',
-                    'number_of_fields',
-                    'reset_value',
-                    'software_value',
-                    'radix',
-                    'radix_width',
-                    'radix_resolution']    # EK: TODO field_name is missing in this list ? Use same order as in ARGS doc.
-                                           # EK: TODO how does this list relate to self._valid_dict() in fields.py, is this a duplicate check?
+                    'reset_value']    # EK: TODO field_name is missing in this list ? Use same order as in ARGS doc.
+                                      # EK: TODO how does this list relate to self._valid_dict() in fields.py, is this a duplicate check?
 
 VALID_DEFAULT_FIELD_KEYS = ['field_description',
-                            'width',
+                            'number_of_fields',
+                            'address_offset',
+                            'mm_width',
                             'access_mode',
                             'side_effect',
-                            'address_offset',
-                            'number_of_fields',
                             'reset_value']   # EK: TODO change to MANDATORY_FIELD_KEYS to have smaller list ? Current list is incomplete.
 
-VALID_FIELD_RADIXS = ['unsigned', 'signed', 'hexadecimal', 'char', 'complx']  # EK: TODO change complx into complex, but complex gets misinterpreted by Python
+# EK: Use alphabetical order of radixs
+VALID_FIELD_RADIXS = ['char8',
+                      'cint16_ir',
+                      'cint16_ri',
+                      'int32',
+                      'int64',
+                      'uint32',
+                      'uint64']  # EK: TODO change complx into complex, but complex gets misinterpreted by Python --> no with complex_ri and comple_ir this is no longer an issue.
 
 
-UNASSIGNED_ADDRESS = 16384     # EK: TODO why is this needed ? Should be -1 for undefined, or just default 0 ?
-UNASSIGNED_BIT = 32            # EK: TODO why is this needed ? Name may be bad choice, use WORD_WIDTH instead?
+UNASSIGNED_ADDRESS = 16384     # EK: TODO why is this needed ? Remove ?
+UNASSIGNED_BIT = 32            # EK: TODO why is this needed ? Remove ?
 
 DEFAULT_NUMBER_OF_PERIPHERALS  = 1
-DEFAULT_NUMBER_OF_SLAVES       = 1
-DEFAULT_SLAVE_DESCRIPTION      = 'NONE'
+DEFAULT_NUMBER_OF_MM_PORTS     = 1
+DEFAULT_MM_PORT_DESCRIPTION    = 'NONE'
 DEFAULT_ADDRESS_LENGTH         = 1
 
-DEFAULT_FIELD_WIDTH            = WORD_WIDTH
-DEFAULT_FIELD_USER_WIDTH       = WORD_WIDTH
-DEFAULT_FIELD_BIT_OFFSET       = UNASSIGNED_BIT           # EK: TODO should be 0 ?
+DEFAULT_FIELD_MM_WIDTH         = MM_BUS_WIDTH
+DEFAULT_FIELD_USER_WIDTH       = MM_BUS_WIDTH
+DEFAULT_FIELD_USER_WORD_ORDER  = 'le'
+DEFAULT_FIELD_BIT_OFFSET       = 0#UNASSIGNED_BIT           # EK: TODO should be 0 ?
 DEFAULT_FIELD_ACCESS_MODE      = 'RW'
 DEFAULT_FIELD_SIDE_EFFECT      = None
 DEFAULT_FIELD_ADDRESS_OFFSET   = UNASSIGNED_ADDRESS       # EK: TODO should be 0 ?
 DEFAULT_FIELD_NUMBER_OF_FIELDS = 1
 DEFAULT_FIELD_RESET_VALUE      = 0
-DEFAULT_FIELD_SOFTWARE_VALUE   = 0
-DEFAULT_FIELD_RADIX            = 'unsigned'
-DEFAULT_FIELD_RADIX_WIDTH      = 0           # 0 : radix_width = field width
-DEFAULT_FIELD_RADIX_RESOLUTION = 0           # 0 : not applicable or resolution is 2**0 = 1
+DEFAULT_FIELD_RADIX            = 'uint32'
+DEFAULT_FIELD_RESOLUTION_W     = 0           # 0 : not applicable or 2**resolution_w = 2**0 = 1
 DEFAULT_FIELD_DESCRIPTION      = 'NONE'
 
-DATA_WIDTH               = 32   # EK: TODO purpose ? use WORD_WIDTH instead ?
+DATA_WIDTH               = 32   # EK: TODO purpose ? use MM_BUS_WIDTH instead ?
 
 
diff --git a/py_args_lib/peripheral_lib/field.py b/py_args_lib/peripheral_lib/field.py
index f24cba99d1af4fc1ab82a6a7b34604318c449fc2..721fb9efc49200d30c711348ba1e64c22621d84b 100644
--- a/py_args_lib/peripheral_lib/field.py
+++ b/py_args_lib/peripheral_lib/field.py
@@ -44,31 +44,29 @@ class Field(BaseObject):
 
         self._valid_dict = {'field_name'        : {},
                             'field_description' : {},
-                            'width'             : {'max': 32, 'min': 1},       # EK: TODO can this be larger e.g. 64 or 256 or don't care {}?
-                            'user_width'        : {'max': 2048, 'min': 32},
+                            'mm_width'          : {'max': 32, 'min': 1},       # EK: TODO can this be larger e.g. 64 or 256 or don't care {}?
                             'bit_offset'        : {'max': UNASSIGNED_BIT, 'min': 0},
+                            'user_width'        : {'max': 2048, 'min': 1},
+                            'user_word_order'   : {},
                             'access_mode'       : {},
                             'side_effect'       : {},
                             'address_offset'    : {'max': 262144, 'min': 0, 'word_aligned': True},
                             'number_of_fields'  : {'max': 262144, 'min': 1},
                             'reset_value'       : {'max': 131071},
-                            'software_value'    : {},
                             'radix'             : {},
-                            'radix_width'       : {},
-                            'radix_resolution'  : {}}
+                            'resolution_w'      : {}}
 
         self._args.update({'field_description': DEFAULT_FIELD_DESCRIPTION,
-                           'width'            : DEFAULT_FIELD_WIDTH,
+                           'mm_width'         : DEFAULT_FIELD_MM_WIDTH,
+                           'user_word_order'  : DEFAULT_FIELD_USER_WORD_ORDER,
                            'bit_offset'       : DEFAULT_FIELD_BIT_OFFSET,
                            'access_mode'      : DEFAULT_FIELD_ACCESS_MODE,
                            'side_effect'      : DEFAULT_FIELD_SIDE_EFFECT,
                            'address_offset'   : DEFAULT_FIELD_ADDRESS_OFFSET,
                            'number_of_fields' : DEFAULT_FIELD_NUMBER_OF_FIELDS,
                            'reset_value'      : DEFAULT_FIELD_RESET_VALUE,
-                           'software_value'   : DEFAULT_FIELD_SOFTWARE_VALUE,
                            'radix'            : DEFAULT_FIELD_RADIX,
-                           'radix_width'      : DEFAULT_FIELD_RADIX_WIDTH,
-                           'radix_resolution' : DEFAULT_FIELD_RADIX_RESOLUTION,
+                           'resolution_w'     : DEFAULT_FIELD_RESOLUTION_W,
                            'group_name'       : None})
 
         if settings is not None:
@@ -92,6 +90,9 @@ class Field(BaseObject):
     def __lt__(self, other):
         return self.address_offset() < other.address_offset()
 
+    # EK TODO why are all these field key methods slightly different, is there not a common structure?
+    # EK TODO check parameter passing to user_width.
+
     def number_of_fields(self, val=None):
         """ set/get number of fields """
         if val is not None:
@@ -108,18 +109,16 @@ class Field(BaseObject):
             return None
         return self._as_str('group_name')
 
-    def width(self, val=None):
-        """ set/get width of field
-        val: if not None set width of field
-        return: actual width of field """
+    def mm_width(self, val=None):    # EK: TODO method name seems to have to be the same as the key name, is that intended?
+        """ set/get mm_width of field
+        val: if not None set mm_width of field
+        return: actual mm_width of field """
         if val is not None:
-            return self.set_kv('width', val)
-        _val = self._as_int('width')
-
-        if not self.is_valid('width'):
+            return self.set_kv('mm_width', val)
+        _val = self._as_int('mm_width')            # EK TODO _val is not used
+        if not self.is_valid('mm_width'):
             return None
-
-        return self._as_int('width')
+        return self._as_int('mm_width')
 
     def bit_offset(self, val=None):
         """ set/get bit_offset of field
@@ -180,22 +179,12 @@ class Field(BaseObject):
             return None
         return self._as_int('reset_value')
 
-    def software_value(self, val=None):
-        """ set/get software reset value of field
-        val: if not None set default value of field
-        return: active software reset value of field """
-        if val is not None:
-            return self.set_kv('software_value', val)
-        if not self.is_valid('software_value'):
-            return None
-        return self._as_int('software_value')
-
     def radix(self, val=None):
         """ set/get radix value of field
         val: if not None set default value of field
         return: active radix value of field """
         if val is not None:
-            #print(val)
+            #print(val.lower())
             if val.lower() in VALID_FIELD_RADIXS:
                 return self.set_kv('radix', val.lower())
             else:
@@ -204,25 +193,15 @@ class Field(BaseObject):
                 return False
         return self._as_int('radix')
 
-    def radix_width(self, val=None):
-        """ set/get radix_width value of field
-        val: if not None set default value of field
-        return: active radix_width value of field """
-        if val is not None:
-            return self.set_kv('radix_width', val)
-        if not self.is_valid('radix_width'):
-            return None
-        return self._as_int('radix_width')
-
-    def radix_resolution(self, val=None):
-        """ set/get radix_resolution value of field
+    def resolution_w(self, val=None):
+        """ set/get resolution_w value of field
         val: if not None set default value of field
-        return: active radix_resolution value of field """
+        return: active resolution_w value of field """
         if val is not None:
-            return self.set_kv('radix_resolution', val)
-        if not self.is_valid('radix_resolution'):
+            return self.set_kv('resolution_w', val)
+        if not self.is_valid('resolution_w'):
             return None
-        return self._as_int('radix_resolution')
+        return self._as_int('resolution_w')
 
     def field_description(self, val=None):
         """ set/get description of field
@@ -232,17 +211,47 @@ class Field(BaseObject):
             return self.set_kv('field_description', val)
         return self._as_str('field_description')
 
+    #def user_width(self, val=None):
+    #    """ set/get width of RAM MM bus side interface  """
+    #    if val is not None:
+    #        self.set_kv('user_width', val)
+    #        return
+    #    return self._as_int('user_width', default=32)
+
     def user_width(self, val=None):
-        """ set/get width of RAM MM bus side interface  """
+        """ set/get user_width value of field
+        val: if not None set default value of field
+        return: active user_width value of field """
+        if val is not None:
+            return self.set_kv('user_width', val)
+        if not self.is_valid('user_width'):
+            return None
+        return self._as_int('user_width')
+
+    def user_word_order(self, val=None):
+        """ set/get user_word_order """
         if val is not None:
-            self.set_kv('user_width', val)
+            self.set_kv('user_word_order', val)
             return
-        return self._as_int('user_width', default=32)
+        return self._as_int('user_word_order')
+
+    def user_word_order(self, val=None):
+        """ set/get user_word_order of field
+        val: if not None and a valid user word order then set user_word_order of field
+        return: actual user_word_order of field """
+        if val is not None:
+            if val.lower() in VALID_FIELD_USER_WORD_ORDERS:
+                return self.set_kv('user_word_order', val.lower())
+            else:
+                logger.error("unknown user_word_order '%s'", val)
+                self.success = False
+                return False
+        return self._as_str('user_word_order').lower()
 
     def base_address(self, val=None):
         """ set/get base_address in bytes """
         if val is not None:
-            if mod(val, WORD_SIZE):  # don't need check here if tool calcs are correct
+            if mod(val, MM_BUS_SIZE):  # don't need check here if tool calcs are correct
                 logger.error("Base address for field {} is not word aligned".format(field.name()))
             self.set_kv('base_addr', val)
             return
@@ -254,16 +263,16 @@ class Field(BaseObject):
         _max = self._valid_dict[yaml_key].get('max', None)
         check_alignment = self._valid_dict[yaml_key].get('word_aligned', False)
 
-        if yaml_key in ['software_value', 'reset_value']:
+        if yaml_key in ['reset_value']:
             _min = 0
-            _max = int(1 << self._args.get('width', 32)) - 1
+            _max = int(1 << self._args.get('mm_width', 32)) - 1
 
         _val = self._as_int(yaml_key)
         if not isinstance(_val, int):
             return True  # may not have been evaluated from parameter yet, skip
 
         if check_alignment:
-            if mod(_val, WORD_SIZE):
+            if mod(_val, MM_BUS_SIZE):
                 logger.error("Address offset for field %s is not word aligned (%s=%s)",
                              self.name(), yaml_key, str(_val))
                 sys.exit()
diff --git a/py_args_lib/peripheral_lib/fifo.py b/py_args_lib/peripheral_lib/fifo.py
index 56200c634c2ed929e6fa5b4d7a59eef24b886fc9..04b00d755068fd0b76f441a1a24ece64aad61540 100644
--- a/py_args_lib/peripheral_lib/fifo.py
+++ b/py_args_lib/peripheral_lib/fifo.py
@@ -25,7 +25,7 @@
 ###############################################################################
 
 import logging
-from constants import WORD_SIZE
+from constants import MM_BUS_SIZE
 from common_func import ceil_pow2
 from field import Field
 from args_errors import ARGSModeError
@@ -53,7 +53,7 @@ class FIFO(Field):
     def update_address_length(self):
         """ update total address_length of Register in bytes """
         n_fields = ceil_pow2(self.number_of_fields())
-        self.set_kv('address_length', n_fields * WORD_SIZE)
+        self.set_kv('address_length', n_fields * MM_BUS_SIZE)
 
     def address_length(self, val=None):
         """ set/get address_length of register in bytes
@@ -62,4 +62,4 @@ class FIFO(Field):
         """
         if val is not None:
             return self.set_kv('address_length', val)
-        return self._as_int('address_length', default=WORD_SIZE)
+        return self._as_int('address_length', default=MM_BUS_SIZE)
diff --git a/py_args_lib/peripheral_lib/ram.py b/py_args_lib/peripheral_lib/ram.py
index a82221e32887f00b6373f66933ad0781b6feb13c..3322952793e164069985f0abdd8bfa83dbc4c842 100644
--- a/py_args_lib/peripheral_lib/ram.py
+++ b/py_args_lib/peripheral_lib/ram.py
@@ -35,7 +35,7 @@ from field import Field
 logger = logging.getLogger('main.perip.ram')
 
 
-MIN_BRAM_DEPTH = 128
+MIN_BRAM_DEPTH = 128   # EK TODO remove this limitation
 
 class RAM(Field):
     """ A RAM is a Field that is repeated address_length times
@@ -46,8 +46,8 @@ class RAM(Field):
         self.name(name)
         self.description = ""
 
-        self._valid_keys = ['number_of_mm_ports', 'user_width']
-        self._args.update({'number_of_mm_ports' : DEFAULT_NUMBER_OF_SLAVES})
+        self._valid_keys = ['number_of_mm_ports', 'mm_width', 'user_width']
+        self._args.update({'number_of_mm_ports' : DEFAULT_NUMBER_OF_MM_PORTS})
 
     def number_of_mm_ports(self, val=None):
         """ set/get number_of_mm_ports """
@@ -68,8 +68,8 @@ class RAM(Field):
     def update_address_length(self):
         """ update total address_length of Register in bytes """
         n_fields = ceil_pow2(self.number_of_fields())
-        self.user_depth(n_fields * self.width() / self.user_width())
-        self.set_kv('address_length', n_fields * WORD_SIZE)
+        #self.user_depth(n_fields * self.mm_width() / self.user_width())   # EK TODO remove this line --> better generalize REG, RAM and FIFO
+        self.set_kv('address_length', n_fields * MM_BUS_SIZE)
 
     def address_length(self, val=None):
         """ set/get address_length of register in bytes
@@ -77,4 +77,4 @@ class RAM(Field):
         return: address_length of register """
         if val is not None:
             return self.set_kv('address_length', val)
-        return self._as_int('address_length', default=WORD_SIZE)
+        return self._as_int('address_length', default=MM_BUS_SIZE)
diff --git a/py_args_lib/peripheral_lib/register.py b/py_args_lib/peripheral_lib/register.py
index 1e89c8a05b898bbb7e772bf1abaa35bbcb6af0b9..f07008711f1ac326f6a9182e67d82368c7ce1d2f 100644
--- a/py_args_lib/peripheral_lib/register.py
+++ b/py_args_lib/peripheral_lib/register.py
@@ -50,17 +50,14 @@ class Register(BaseObject):    # EK: TODO rename to REG(), to match RAM and FIFO
 
         self._valid_keys = ['number_of_mm_ports', 'address_length', 'mm_port_description', 'base_address']  # EK: TODO why here and not in constants.py, best use only one place
 
-        self._args.update({'number_of_mm_ports' : DEFAULT_NUMBER_OF_SLAVES,
-                           'address_length'   : DEFAULT_ADDRESS_LENGTH,          # EK: TODO strange address_length is not an ARGS yaml key, so why in this list?
+        self._args.update({'number_of_mm_ports' : DEFAULT_NUMBER_OF_MM_PORTS,
+                           'address_length'   : DEFAULT_ADDRESS_LENGTH,          # EK: TODO address_length is not an ARGS yaml key, so why in this list?
                            'dual_clock'       : False,
-                           'mm_port_description': DEFAULT_SLAVE_DESCRIPTION})      # EK: TODO what is the purpose of this list, it seems incomplete.
+                           'mm_port_description': DEFAULT_MM_PORT_DESCRIPTION})      # EK: TODO what is the purpose of this list, it seems incomplete.
 
         # self.update_address_length()
-        self.isIP = False        # EK: TODO remove platform specific isIP
         self.mm_port_span = None
 
-        self.protocol = 'LITE'   # EK: TODO remove AXI specific protocol field (LITE, FULL)
-
     def number_of_mm_ports(self, val=None):
         """ set/get number_of_mm_ports """
         if val is not None:
@@ -87,11 +84,11 @@ class Register(BaseObject):    # EK: TODO rename to REG(), to match RAM and FIFO
 
         n_bytes = 0
         if any(self.fields):
-            n_bytes += max(max([_field.address_offset() for _field in self.fields]) + WORD_SIZE, self.mm_port_span if self.mm_port_span is not None else 0)
+            n_bytes += max(max([_field.address_offset() for _field in self.fields]) + MM_BUS_SIZE, self.mm_port_span if self.mm_port_span is not None else 0)
         
         if any(self.rams):
-            #n_bytes += max(max([_field.address_offset() for _field in self.rams]) + WORD_SIZE, self.mm_port_span if self.mm_port_span is not None else 0)
-            n_bytes += ceil_pow2(self.rams[0].number_of_fields()) * WORD_SIZE
+            #n_bytes += max(max([_field.address_offset() for _field in self.rams]) + MM_BUS_SIZE, self.mm_port_span if self.mm_port_span is not None else 0)
+            n_bytes += ceil_pow2(self.rams[0].number_of_fields()) * MM_BUS_SIZE
 
         self.set_kv('address_length', n_bytes)
 
@@ -100,7 +97,7 @@ class Register(BaseObject):    # EK: TODO rename to REG(), to match RAM and FIFO
         val: if not None set address_length of register
         return: address_length of register """
         if val is not None:
-            if mod(val, WORD_SIZE):  # dont need this if properly calcd
+            if mod(val, MM_BUS_SIZE):  # dont need this if properly calcd
                 logger.error("Invalid address length for register {}, not word aligned".format(self.name()))
                 sys.exit()
             return self.set_kv('address_length', val)
diff --git a/slave_port_naming.txt b/slave_port_naming.txt
index f52542702ac413b1b08e790e05afc0c0f6d4a25e..36d9661a7b891fa26eee05a276140b7ab6e3049f 100644
--- a/slave_port_naming.txt
+++ b/slave_port_naming.txt
@@ -19,7 +19,7 @@
 -------------------------------------------------------------------------------
 --
 -- Author: E. Kooistra
--- Purpose: Investigate slave port naming for ARGS
+-- Purpose: Investigate peripheral and MM port naming for ARGS
 --
 
 ###############################################################################
@@ -28,7 +28,7 @@
 
 1) Peripherals:
 
-  The peripheral_name key groups one or more slaves and identifies the Python
+  The peripheral_name key groups one or more MM ports and identifies the Python
   peripheral script in $UPE_GEAR/peripherals:
 
   . pi_<peripheral_name>.py
@@ -66,17 +66,17 @@
   peripheral_group is the instance label of the peripheral_name.
 
 
-2) Slave port naming
+2) MM port naming
 
-  Do not use the structure slave port naming scheme of ARGS, instead keep on
-  using arbitrary slave_port_name[] naming, because the names have to match
-  the slave port names used in QSYS and in pi_<peripheral_name>.py.
+  Do not use the structure MM port naming scheme of ARGS, instead keep on
+  using arbitrary mm_port_name[] naming, because the names have to match
+  the MM port names used in QSYS and in pi_<peripheral_name>.py.
 
   In the mmap the periperals are not identified by their peripheral_name,
-  but instead by the names of their slave ports. Therefore the arbitrary
-  slave_port_name must be unique within the whole mmap of the fpga.yaml.
+  but instead by the names of their MM ports. Therefore the arbitrary
+  mm_port_name must be unique within the whole mmap of the fpga.yaml.
   Hence the peripheral_name is not uses as a level of hierarchy, instead the
-  names of the slave ports within a peripheral have to be unique within the
+  names of the MM ports within a peripheral have to be unique within the
   fpga.yaml.
 
   The multiple instances of a peripheral are distinguished in
@@ -86,17 +86,17 @@
   . an array index [] as post fix if the instances form an array, so a
     list of related instances
    
-  Use the following structured slave port naming fields in the mmap format:
+  Use the following structured MM port naming fields in the mmap format:
 
-  . slave_name       = name of the slave port
-  . peripheral_group = optional instance name of the slave port, in case the
+  . mm_port_name       = name of the MM port
+  . peripheral_group = optional instance name of the MM port, in case the
                        peripheral is used in multiple independent instances
 
   Manually ensure that:
 
-  . slave_port_name = <slave_name>_<peripheral_group>
+  . mm_port_name = <mm_port_name>_<peripheral_group>
   
-  The slave_name shows that different instances are in fact similar slaves,
+  The mm_port_name shows that different instances are in fact similar mm_ports,
   and the peripheral_group makes these instances unique within the fpga.yaml.
 
 3) Arrays
@@ -104,23 +104,23 @@
   There can be two levels of arrays in ARGS:
   
   . number_of_peripherals in fpga.yaml
-  . number_of_slaves in peripheral.yaml
+  . number_of_mm_ports in peripheral.yaml
 
-  However, with the arbitrary slave_port_name naming it is not possible to
+  However, with the arbitrary mm_port_name naming it is not possible to
   distinghuis between these two different array levels, because the
-  arbitrary slave_port_name naming only supports one array index. Therefore
+  arbitrary mm_port_name naming only supports one array index. Therefore
   flatten the array index to one dimension:
   
-    . [number_of_peripherals][number_of_slaves] =
-      [number_of_peripherals * number_of_slaves]
+    . [number_of_peripherals][number_of_mm_ports] =
+      [number_of_peripherals * number_of_mm_ports]
 
   so that there is only one array index [] in:
    
-    . slave_port_name = <slave_name>_<peripheral_group>[]
+    . mm_port_name = <mm_port_name>_<peripheral_group>[]
 
   For the base addresses it depends on the QSYS whether the base address
   increments per with the span of the peripheral or with the span of the
-  slave.
+  mm_port.
 
 
 4) Summary
@@ -129,13 +129,13 @@ a) Keep number_of_peripherals in fpga.yaml, because this second array level is n
 b) Rename some keys in fpga.yaml to more clearly distinguish them from similar keys in periperal.yaml:
    - periperal_name --> fpga_periperal_name
    - periperal_group --> fpga_periperal_group
-   - slave_port_names --> fpga_slave_port_names
-c) Remove structured slave port naming:
-   - instead only use the manual fpga_slave_port_names
-   - give a warning if fpga_slave_port_name != <slave_name>_<fpga_periperal_group>, to promote manual structured slave port naming
+   - mm_port_names --> fpga_mm_port_names
+c) Remove structured MM port naming:
+   - instead only use the manual fpga_mm_port_names
+   - give a warning if fpga_mm_port_name != <mm_port_name>_<fpga_periperal_group>, to promote manual structured MM port naming
 d) Do not mix generic ARGS code with platform specific code:
-   - Use dedicated new slave_type to support e.g. AXI Lite registers, so keep the slave_type REG generic
-   - Remove slave_protocol
+   - Use dedicated new mm_port_type to support e.g. AXI Lite registers, so keep the mm_port_type REG generic
+   - Remove mm_port_protocol
 e) field
    - use exaxt yaml names in the Python code, so then e.g ?:
      . periperal.name --> periperal.periperal_name
@@ -240,7 +240,7 @@ e) field
          32b field, via the low side.
        . is radix_width still needed if field width can be increased in _valid_dict in field.py?
        
-   - Fields can be treated same for all three slave_type, because:
+   - Fields can be treated same for all three mm_port_type, because:
      . Typically RAM and FIFO only have one field in fields, REG can have multiple fields,
        however also for RAM and FIFO it can be useful to define multiple fields to define
        a pattern.
@@ -248,19 +248,19 @@ e) field
        field_group with number_of_field_groups key can define a repeating pattern
      
 f) use hdl_library_name/peripheral_name to instantiate a periperal_name in an fpga.yaml
-   - the peripheral_name level of hierarchy is not used name the slave on the MM bus, instead the periperal_name is used to get to the slave_name. The slave_name is used to name the slave on the MM bus, therefore the slave_name has to be unique. Unique slave_names are achieved manually by using the slave_type and hdl_library_name in the slave name.
-   - use manual slave port naming in the fpga.yaml
+   - the peripheral_name level of hierarchy is not used name the MM port on the MM bus, instead the periperal_name is used to get to the mm_port_name. The mm_port_name is used to name the MM port on the MM bus, therefore the mm_port_name has to be unique. Unique mm_port_names are achieved manually by using the mm_port_type and hdl_library_name in the mm_port_name.
+   - use manual MM port naming in the fpga.yaml
 g) Warnings to check preferred naming when:
-   - slave_type should be in slave_name  (reg=pio, ram, fifo)
-   - peripheral_name should be in slave_name (e.g. remu and reg_remu)
-   - slave_name should be in slave_port_name
-   - peripheral_group should be in slave_port_name to distinguish different slave_name instances
-   - The hdl_library_name does not have to be in the slave_port_name (e.g. unb2b_board and ram_scrap), but can be (e.g. remu and reg_remu)
+   - mm_port_type should be in mm_port_name  (reg=pio, ram, fifo)
+   - peripheral_name should be in mm_port_name (e.g. remu and reg_remu)
+   - mm_port_name should be in mm_port_name
+   - peripheral_group should be in mm_port_name to distinguish different mm_port_name instances
+   - The hdl_library_name does not have to be in the mm_port_name (e.g. unb2b_board and ram_scrap), but can be (e.g. remu and reg_remu)
    - field_group should be in field_name to distinguish different field_names
    Errors:
-   - slave_port_names in fpga.yaml must be unique
+   - mm_port_names in fpga.yaml must be unique
    - peripheral_names in peripheral.yaml must be unique
-   - slave_names in peripheral.yaml must be unique
+   - mm_port_names in peripheral.yaml must be unique
    
 h) mmap
    - only base address and span in mmap or do also we need the fields in mmap?
@@ -275,9 +275,9 @@ h) mmap
    - only non-redundant info in mmap, not needed now:
      . hdl_library_name,
        peripheral_name,
-       peripheral_group, because the slave port name uniquely defines the MM port, no need to know its related origin keys,
+       peripheral_group, because the MM port name uniquely defines the MM port, no need to know its related origin keys,
      . parameters values, because parameters are filled in and thus have become fixed in mmap.
-     . slave_name, because each slave is treated as it is, no need reuse code for >1 instances are in fact similar
+     . mm_port_name, because each MM port is treated as it is, no need reuse code for >1 instances are in fact similar
      . endianness, because it values are passed on transparently, mmap does not change the endianess
      . field width, bit_offset, radix width, because these are / can be encorporated in bit mask.
        The maximum field width is 32b, but the bit mask can extend beyond 31 when bit_offset + radix_width > 32, this
@@ -321,85 +321,285 @@ h) mmap
          . With QSYS, QSYS determines the peripheral base addresses.
        - peripheral_span   # number of addresses (in words) used by one peripheral, an array of
                            # peripherals has span = number_of_peripherals * 2**ceil_log2(peripheral_span)
-       - slave_span        # number of addresses (in words) used by one slave MM port, an array of
-                           # MM ports has span = number_of_slaves * 2**ceil_log2(slave_span)
-                           # . The slave_span follows from the last field: address_offset, number_of_fields
+       - mm_port_span        # number of addresses (in words) used by one MM port, an array of
+                           # MM ports has span = number_of_mm_ports * 2**ceil_log2(mm_port_span)
+                           # . The mm_port_span follows from the last field: address_offset, number_of_fields
                            #   width, bit_offset, radix_width.
-                           # . Keep slave_span key to enforce a it from peripheral.yaml in case the span
+                           # . Keep mm_port_span key to enforce a it from peripheral.yaml in case the span
                            #   needs to be larger than needed.
    - already existing columns:
      . field radix           # radix type
      . field radix bit mask  # derived from field width, bit_offset, radix_width
    - new column:
      . radix packing         # = nof_radix_per_field derived from field width, radix_width. Is >= 1.
+                             # not used --> instead use mm_mask and user_mask
+
+i) bit_mask
+   . address_offset
+   . bit_offset
+   . number_of_fields
+
+   TODO
+   . Done: width --> mm_width
+   . Done: user_width <--radix_width
+   . Done: remove radix packing, because follows from mm_mask and user_mask
+   . Done: WORD_WIDTH, WORD_SIZE --> MM_BUS_WIDTH, MM_BUS_SIZE
+   . Done: address span --> number_of_fields
+   . Done: bit_mask --> mm_mask
+   . Done: add user_mask, only list user_mask if it differs from mm_mask, if the same then use '-'
+   . Done: add endianess --> user_word_order
+   . Done: check field addresses --> f_base in gen_rom_mmap.py is ok.
+   . Done: define radix inclusing size in bits to reveal that there is a second MM word address when
+           that user_width > mm_width and to suggest the type for the host.
+   . Done: rename radix_resolution --> user_resolution_w
+   . Done: support mm_mask and user_mask for RAM and FIFO
+   . check base addresses obtained from QSYS
+   . use bash to remove redundant spaces from mmap file, before putting it in the FPGA system info ROM
+   . support field_name for RAM and FIFO (no fixed 'data' in gen_rom_mmap.py)
+
+   MM_BUS_WIDTH = 32    # fixed
+   width --> mm_width   # >= 1 and <= MM_BUS_WIDTH
+
+   packed = True or False # not needed, because derived from mm_width // user_width
+
+   # support 1 <= mm_width <= MM_BUS_WIDTH = 32
+   #   . default mm_width = MM_BUS_WIDTH = 32
+   #   . unused MSbits in MM_BUS_WIDTH are undefined, so it is not sure whether they are e.g. 0 or sign extended
+   # support any user_width >= 1
+   #   . default user_width = mm_width
+   # default MM word and user word are little endian.
+   #   . endianess applies to host, MM protocol, MM bus and user fields (FPHA logic)
+   #     - the host and MM protocol endianess are dont care / not defined by ARGS
+   #     - the MM word little endianess is a constant in ARGS code MM_BUS_ENDIANESS = le
+   #     - the user fields in the FPGA are all little endian sofar, but could be big endian for future IP.
+   #     - FPGA user logic takes care of applying network order in case fields are used in packet headers
+   #   . support both MM word and user field are big endian ?
+   # support bit_offset:
+   #   . can have multiple fields per MM word --> access_mode: RW or RO for MM word, only RO for bit fields, no need for RMW = Read Modify Write
+   #   . only for fields that fit in a single MM word, so user_width < mm_width
+   #   . only for number_of_fields = 1
+   #   . examples:
+   #     - info and sub fields in unb2b_board
+   # use mm_width, user_width for standard little or big endian,
+   #   . default mm_width = MM_BUS_WIDTH = 32
+   #   . endianess MM word and endianess of multi MM words are the same (l = L,l or b = B,b)
+   #   . to have single line in mmap also for multi MM word fields, the mm_mask applies to all MM words
+   #   . only one field per MM word
+   #   . for number_of_fields == 1
+   #   . for number_of_fields > 1
+   #     - ignore bit_offset (= 0)
+   #     - only support user_word_order = 0 = 'le' and user_byte_order = 0 = 'le', because then the user word with w > 32 bit can be
+   #       represented by [w-1:0] on a single line in the MMAP file
+   #     - add user_word_order as yaml key
+   #     - no need for user_byte_order as yaml key, because user_byte_order = 0 = 'le' is the default user byte order for all
+   #       fields in the FPGA logic so far.
+   #   . examples:
+   #     - bsn[63:0] in dp
+   #     - eth_source_mac[47:0] in sdp
+   # future e.g. support mm_hi, mm_lo, user_hi and user_lo:
+   #   . ignore bit_offset (= 0)
+   #   . only needed when field extends in more than one MM word, so when user_width + bit_offset would be > mm_width
+   #   . can have multiple fields per MM word --> access_mode: RW or RO for MM word, only RO for bit fields, no need for RMW = Read Modify Write
+   #   . for special offsets and for mixed endianess (e.g. L,b or B,l or other),
+   #   . only for number_of_fields = 1
+   #   . with dedicated line in yaml file for each MM word part of the user field --> use tuple or list to define the hi, lo values for all words ?
+   #   . with dedicated line in MMAP file for multi MM word fields
+   #     - no need to add number_of_mm_word_per_field column to signal in MMAP that the user field occupies multiple MM words:
+   #       . if the next line contains more bit fields for the last field then these are combined into one larger field.
+   #       . number_of_fields > 1 is not supported when the field cannot be expressed on a single line, which is sufficient for now
+   #   . examples:
+   #     - tx_stats_*[35:0] in nw_10GbE
+   # support field packing into MM words
+   #   . ignore bit_offset (= 0)
+   #   . for number_of_fields > 1 and user_width < mm_width
+   #   . array order from low to high byte addresses
+   #   . user_mask follows from user_width
+   #   . mm_mask follows from nof_fields_per_mm_word = mm_width // user_width
+   #   . examples:
+   #     - design_name[7:0] * number_of_fields = 52 char string with nof_fields_per_mm_word = 4 char / 32 bit MM word in unb2b_board
+
+   A) user_width >= mm_width
+
+   reg[47:0] = 0x060504030201  little endian = LSByte at byte address 0
+               0x010203040506  big endian  = MSByte at byte address 0
+
+
+   YAML                                                                            MMAP
+   endian  address  bit     mm     user    mm      user    user        mm_value     mm_mask  user_mask  endianess
+           offset   offset  width  width   hi lo   hi lo   endianess
+
+   L,l     0        0       32     48      31  0   31  0   l           0x04030201   [31:0]   [31:0]     l            # eth_source_mac
+           4                               15  0   47 32   l           0x....0605   [15:0]   [47:32]    l
+
+   L,l     0        0       32     48      31  0   47  0   l           0x04030201   [31:0]   [47:0]     l            # support single line mmap format for L,l
+                                                                       0x....0605
+
+
+   L,l     0        0       24     48      23  0   23  0   l           0x..030201   [23:0]   [23:0]     l
+           4                               23  0   47 24   l           0x..060504   [23:0]   [47:24]    l
+
+   B,b     0        0       32     48      31  0   47 16   b           0x03040506   [31:0]   [47:16]    b
+           4                               15  0   15  0   b           0x....0102   [15:0]   [15:0]     b
+
+   B,b     0        0       32     48      31  0   47  0   b           0x03040506   [31:0]   [47:0]     b            # support single line mmap format for B,b
+                                                                       0x....0102
+
+   B,b     0        0       24     48      23  0   47 24   b           0x..040506   [23:0]   [47:24]    b
+           4                               23  0   23  0   b           0x..010203   [23:0]   [23:0]     b
+
+
+
+   B,l     0        0       32     48      15  0   47 32   l           0x....0605   [15:0]   [47:32]    l            # nw_10GbE, requires masks per MM address
+           4                               31  0   31  0   l           0x04030201   [31:0]   [31:0]     l
+
+   B,l     0       16       32     48      31  0   47 16   l           0x06050403   [31:0]   [47:16]    l
+           4                               31 16   15  0   l           0x0201....   [31:16]  [15:0]     l
+
+   L,b     0        0       32     48      15  0   15  0   b           0x....0102   [15:0]   [15:0]     b
+           4                               31  0   47 16   b           0x03040506   [31:0]   [47:16]    b
+
+   L,l     0       16       32     48      31  0   31  0   l           0x04030201   [31:0]   [31:0]     l
+           4                               31 16   47 32   l           0x0605....   [31:16]  [47:32]    l
+
+   L,l     0        8       32     48      31  8   23  0   l           0x030201..   [31:8]   [23:0]     l
+           4                               23  0   47 24   l           0x..060504   [23:0]   [47:24]    l
+
+   B) user_width < mm_width --> packing
+
+   YAML                                                      MMAP
+   address  mm     user   user       number_of  mm_value     number_of  mm_mask  user_mask  endianess
+   offset   width  width  endianess  fields                  fields
+
+   0        32      8      l         9          dcba         9          [31:0]   [7:0]      l         # "abcdefghi", packed 4 char / MM word
+                                                hgfe
+                                                ...i
 
+   0        32     16      l         3          0x04030201   3          [31:0]   [7:0]      l         # 0x0201, 0x0403, 0x0605, packed 2 unsigned little endian / MM word
+                                                0x....0605
 
-i) address calculation
+   0        32     16      b         3          0x03040102   3          [31:0]   [7:0]      b         # 0x0201, 0x0403, 0x0605, packed 2 unsigned big endian  / MM word
+                                                0x....0506
+
+
+   # address_span follows from address_offset, number_of_fields, user_width from user_mask
+
+   # bit_mask
+   if number_of_fields == 1:
+       if user_width == None:
+           user_width == mm_width  # default
+           address_span = number_of_fields
+           bit_mask = [user_width:0] + bit_offset
+       if user_width > mm_width
+           address_span = ceil_div(user_width, mm_width) * number_of_fields
+           bit_mask = [user_width:0] + bit_offset
+       if user_width < mm_width
+           nof_fields_per_mm_word = mm_width // user_width
+           address_span = ceil_div(number_of_fields, nof_fields_per_mm_word)
+
+   if number_of_fields > 1:
+       bit_offset not used or applies only to first field and must be multiple of user_width
+
+
+j) endianess per MM word (l, b) and for multi MM word (L, B)
+
+   0 = Ll ^= l, default alle MM is little endian
+   1 = Lb --> komt niet voor
+   2 = Bl --> alleen voor de 36 bit counts in nw_10GbE.nw_10GbE_unb2legacy
+   3 = Bb ^= b, komt niet voor
+
+   endianess column in mmap met enum of number?
+
+
+
+
+k) address calculation
   - Use base addresses from QSYS or calculate base addresses based on peripheral spans
-  - Slave MM port span is ceil_log2(address offset of last field + largest(width, radix_width) / 32)
-  - Peripheral element span is ceil_log2(number_of_slaves * Slave MM port span)
+  - MM port span is largest for all fields of ceil_log2(address offset of field + largest(width, radix_width) / 32)
+  - Peripheral element span is ceil_log2(number_of_mm_ports * MM port span)
   - Peripheral array span is ceil_log2(number_of_peripherals * Peripheral element span)
   - Peripherals in order of QSYS or in increasing order of Peripheral (array) spans (so smallest first)
   - Base address is multiple of Peripheral array span
   - Base address is lock_base_address or next multiple of Peripheral array span
 
-  - Add set peripheral_span key, similar to slave_span key, to support having more span
-    between elements in an array (other name could be lock_peripheral_span, sock_slave_span)
+  - Add set peripheral_span key, similar to mm_port_span key, to support having more span
+    between elements in an array (other name could be lock_peripheral_span, sock_mm_port_span)
     The address calculations should check that the specified span value fits. Default use
     span = 0 to use the calculated minimal required span. The span is the actual number of
     bytes (multiple of 4), the 2**span_w yields the occupied address space.
 
-j) ARGS steps
-  - read in and parse yaml files
-    . check valid keys
-    . check valid values (e.g. address_offset multiple of 4)
-    . do not unroll arrays (number_of_peripherals, number_of_slaves, number_of_fields)
-  - evalutate/substitute the parameters
-    . check that parameter_overrides exist on peripheral
-  - calculate addresses
-    . calculate slave spans and span_w
-    . calculate peripheral spans and span_w
-    . calculate peripheral (array) base addresses or obtain from QSYS
-    . check that the spans fit
-  - gen_rom_mmap.py to create mmap file
-  - gen_doc.py to create ICD document
+l) ARGS steps
+  - read in and parse fpga.yaml file
+    . check yaml syntax
+    . check ARGS syntax
+      - check that the keys in fpga.yaml are valid
+  - read in and parse peripheral.yaml files that are used in the fpga.yaml file
+    . check yaml syntax
+    . check ARGS syntax
+      - check that the keys in peripheral.yaml are valid
+      - check that peripheral instantiations in fpga.yaml exist in the periperal.yaml
+      - check that parameter_overrides in fpga.yaml exist on the peripheral
+      - add default keys with their default value (typically None = not specified -->
+          let gen_* script fill in value dependent on the function of the gen_* script)
+  - evalutate/substitute the parameters overrides
+    . put fields in address_offset order
+    . check valid values
+      - string naming rules (must start with an a-z,A_Z character)
+      - field address_offset multiple of MM_BUS_SIZE = 4 bytes
+      - field bit_offset + mm_width <= MM_BUS_WIDTH = 32 bit
+    . do not unroll arrays (number_of_peripherals, number_of_mm_ports, number_of_fields)
+
+  - ARGS code structure
+    - Read ARGS yaml configuration into Python:
+      yaml parser,
+      fpga uses peripherals,
+      peripherals uses mm_ports,
+      there are multiple mm_port type (REG, RAM, FIFO) but they all use fields.
+    - Generate scripts, e.g.
+      . gen_rom_mmap.py: generate MMAP file for fpga.yaml
+      . gen_doc.py: generate ICD document file for fpga.yaml, better use markdown then
+                    LaTeX, because markdown is simpler and thus easier to maintain.
+      . generate MM bus base addresses for fpga.yaml, based on peripheral and mm_port spans
+      . get MM bus base addresses from QSYS for fpga.yaml
+      . generate MM bus HDL for fpga.yaml
+      . generate MM interface HDL for a periperal or all periperals in a periperal.yaml
+
 
 z) Other:
   - Rename Register.py into reg.py and REG.
   - Numbers (int, float) and boolean (True, TRUE, False) are supported as parameters values,
     but strings are not (yet) supported.
   - Add instance_description key in fpga.yaml to support describing some peripheral instance specifics.
-  - parameter_overrides does not check whether parameter actually exists on periperal
+  - parameter_overrides does not yet check whether parameter actually exists on periperal
 
    
 
 ###############################################################################
 #
-# Slave port naming
+# MM port naming
 #
 ###############################################################################
 
-# The slave port name represents the base address of the slave on the MM
-# bus. Support two types of slave port naming:
+# The MM port name represents the base address of the MM port on the MM
+# bus. Support two types of MM port naming:
 #
-# 1) arbitrary naming to support any slave port name on the MM bus
+# 1) arbitrary naming to support any MM port name on the MM bus
 # 2) structured naming to have a standardized uniform naming convention
 #
-# In both cases the slave port names on MM bus must be unique. The
+# In both cases the MM port names on MM bus must be unique. The
 # structured naming uses '.' as separator to preserve hierarchy information
 # in the name. The format becomes:
 #
-#     Arbitrary  : slave_port_names
-#     Structured : subsystem_name.slave_prefix.slave_name.slave_postfix
+#     Arbitrary  : mm_port_names
+#     Structured : subsystem_name.mm_port_prefix.mm_port_name.mm_port_postfix
 #
 # The structured naming uses 4 levels and to preserve this format all 4 levels
 # should have names that are != ''. Using '' is allowed, but then still the '.'
-# will be in the slave port name to preserve the levels.
+# will be in the MM port name to preserve the levels.
 #
 # The name keys are defined in:
 #
 # - system.yaml
-#     slave_port_names : define list of arbitrary slave port names or use empty
+#     mm_port_names : define list of arbitrary MM port names or use empty
 #                        string to use structured naming
 #     subsystem_name   : extra prefix in case of structured naming, to
 #                        clarify the usage, or to group peripherals or to 
@@ -407,21 +607,21 @@ z) Other:
 #                        instances of this peripheral
 #
 # - peripheral.yaml
-#     slave_prefix        : prefix to slave_name, typically the HDL library
-#                           name of the slave HDL code
-#     slave_name          : functional name of the slave
-#     slave_postfix       : postfix to slave_name, typically the slave_type,
+#     mm_port_prefix        : prefix to mm_port_name, typically the HDL library
+#                           name of the MM port HDL code
+#     mm_port_name          : functional name of the MM port
+#     mm_port_postfix       : postfix to mm_port_name, typically the mm_port_type,
 #                           so REG, RAM, FIFO, PIO (parallel input output 
 #                           = REG)
 #
 #
-# if slave_port_names[]!='':
-#     # Use arbitrary slave port name to identify the base address
-#     mmBusSlaveName = slave_port_name[]
+# if mm_port_names[]!='':
+#     # Use arbitrary MM port name to identify the base address
+#     mmBusMmPortName = mm_port_name[]
 # else:
-#     # Use structured slave port name to identify the base address
-#     mmBusSlaveName = subsystem_name + '.' + slave_prefix + '.' + slave_name + '.' + slave_postfix
-# return mmBusSlaveName
+#     # Use structured MM port name to identify the base address
+#     mmBusMmPortName = subsystem_name + '.' + mm_port_prefix + '.' + mm_port_name + '.' + mm_port_postfix
+# return mmBusMmPortName
 #
 
 ###############################################################################
@@ -453,12 +653,12 @@ parameters :
 peripherals :
     - peripheral_name : string     # reference to peripheral_name in peripheral.yaml
       subsystem_name : string
-      slave_port_names : None     # or list of arbitrary slave port names, must
-                                  # match the order of the slave_ports list in
+      mm_port_names : None     # or list of arbitrary MM port names, must
+                                  # match the order of the mm_ports list in
                                   # the peripheral.yaml
-          - ''                          # use structured slave port name
-          - 'overrule-slave-port-name'  # use aribtrary slave port name
-          - ''                    # use structured slave port name
+          - ''                          # use structured MM port name
+          - 'overrule-mm-port-name'  # use aribtrary MM port name
+          - ''                    # use structured MM port name
       lock_base_address : int # = None
       number_of_peripherals : int # = 1, >1 for array
       parameters : # use default or overrule
@@ -476,8 +676,8 @@ peripherals :
 
 ###############################################################################
 # New format for the peripheral.yaml file
- --> EK 2021-02-08 agrees with ARGS doc, except for missing slave_prefix and 
-     slave_postfix, but that is fine.
+ --> EK 2021-02-08 agrees with ARGS doc, except for missing mm_port_prefix and
+     mm_port_postfix, but that is fine.
      
 protocol_name:  args
 protocol_version : major.minor
@@ -493,12 +693,12 @@ peripherals :
           - {name : string, value : int | float | string}   # peripheral parameter a
           - {name : string, value : int | float | string}   # peripheral parameter b          
           # etc.
-      slave_ports :
-          - slave_prefix : string
-            slave_name : string
-            slave_postfix : string
-            number_of_slaves : int # = 1, >1 for array
-            slave_type : string # REG | RAM | FIFO
+      mm_ports :
+          - mm_port_prefix : string
+            mm_port_name : string
+            mm_port_postfix : string
+            number_of_mm_ports : int # = 1, >1 for array
+            mm_port_type : string # REG | RAM | FIFO
             dual_clock : boolean
             fields :                                       REG RAM FIFO
                 - field_name : string                              
@@ -514,10 +714,10 @@ peripherals :
                   field_description : string                            
                 - field_name : string
                 # etc.
-            slave_description : string
-          - slave_prefix : string
-            slave_name : string
-            slave_postfix : string
+            mm_port_description : string
+          - mm_port_prefix : string
+            mm_port_name : string
+            mm_port_postfix : string
             # etc.
           
       peripheral_description : string
@@ -564,10 +764,10 @@ peripherals :
         <peripheral_parameter_a> : int | float | string
         <peripheral_parameter_b> : int | float | string
         # etc.
-    slave_ports :
-        <slave_port_name_a> :
-            <number_of_slaves> : int # = 1, >1 for array
-            slave_type : string # REG | RAM | FIFO
+    mm_ports :
+        <mm_port_name_a> :
+            <number_of_mm_ports> : int # = 1, >1 for array
+            mm_port_type : string # REG | RAM | FIFO
             fields :
                 <field_name_a> :
                     width : int # = 32
@@ -582,8 +782,8 @@ peripherals :
                     field_description : string
                 <field_name_b> :
                 # etc.
-            slave_description : string
-        <slave_port_name_b> :
+            mm_port_description : string
+        <mm_port_name_b> :
         # etc.
     peripheral_description : string
 ---
@@ -621,7 +821,7 @@ peripherals :
 ###############################################################################
 
 # manual style name            # structured style keys                                               # structured style name
-slave_port_name                slave_prefix  slave_name      slave_postfix  subsystem_name           slave_port_name (4 levels)         slave_port_name (2 levels)
+mm_port_name                   mm_port_prefix  mm_port_name      mm_port_postfix  subsystem_name           mm_port_name (4 levels)            mm_port_name (2 levels)
 
 AVS_ETH_0_MMS_RAM         -->  ETH           -, DATA         RAM            -, MINIMAL      -->      MINIMAL.ETH.DATA.RAM               MINIMAL.ETH_DATA_RAM           
 AVS_ETH_0_MMS_REG         -->  ETH           -, CONTROL      REG            -, MINIMAL      -->      MINIMAL.ETH.CONTROL.REG            MINIMAL.ETH_CONTROL_REG        
@@ -718,7 +918,7 @@ REG_TR_NONBONDED_MESH     -->  TR_NONBONDED  -, CONTROL      REG            MESH
 ###############################################################################
 
 # manual style name                 # structured style keys                                               # structured style name
-slave_port_name                     slave_prefix  slave_name      slave_postfix  subsystem_name           slave_port_name
+mm_port_name                        mm_port_prefix  mm_port_name      mm_port_postfix  subsystem_name           mm_port_name
                                
 AVS_ETH_0_MMS_RAM              -->  ETH           -, DATA         RAM            -, MINIMAL      -->      MINIMAL.ETH.DATA.RAM
 AVS_ETH_0_MMS_REG              -->  ETH           -, CONTROL      REG            -, MINIMAL      -->      MINIMAL.ETH.CONTROL.REG
@@ -804,7 +1004,7 @@ REG_TR_10GBE                   -->  TR_10GBE      -               REG
 ###############################################################################
 
 # manual style name                   # structured style keys                                               # structured style name
-slave_port_name                       slave_prefix  slave_name      slave_postfix  subsystem_name           slave_port_name
+mm_port_name                          mm_port_prefix  mm_port_name      mm_port_postfix  subsystem_name           mm_port_name
                                  
 AVS_ETH_0_MMS_RAM                -->  ETH           -, DATA         RAM            -, MINIMAL      -->      MINIMAL.ETH.DATA.RAM
 AVS_ETH_0_MMS_REG                -->  ETH           -, CONTROL      REG            -, MINIMAL      -->      MINIMAL.ETH.CONTROL.REG
diff --git a/uniboard_rom_system_info.py b/uniboard_rom_system_info.py
index 16c11404730285247f31e1d2d53f6d0fff69da5d..366a5081f3e7bc1ba8ab4129f0a97635cfe46bbe 100755
--- a/uniboard_rom_system_info.py
+++ b/uniboard_rom_system_info.py
@@ -82,23 +82,23 @@ class MemoryMapper(object):
             self.map.append({'name': name,
                              'base_address': base_address,
                              'end_address': end_address,
-                             'size': size * WORD_SIZE,
+                             'size': size * MM_BUS_SIZE,
                              'nof_instances': nof_instances,
                              'hide_in_reg_file': hide_in_reg_file})
 
             logger.debug("add(): %08x, %08x, %d, %s, %d, %s",
-                         base_address, end_address, (size * WORD_SIZE), name, nof_instances, str(hide_in_reg_file))
+                         base_address, end_address, (size * MM_BUS_SIZE), name, nof_instances, str(hide_in_reg_file))
         else:
             _base_address = self._find_start_addr(size*nof_instances)
 
             self.map.append({'name': name,
                              'base_address': _base_address,
                              'end_address': _base_address+(size*nof_instances),
-                             'size': size * WORD_SIZE,
+                             'size': size * MM_BUS_SIZE,
                              'nof_instances': nof_instances,
                              'hide_in_reg_file': hide_in_reg_file})
             logger.debug("add(): %08x, %08x, %d, %s, %d, %s",
-                         _base_address, _base_address+(size*nof_instances), size * WORD_SIZE, name, nof_instances, str(hide_in_reg_file))
+                         _base_address, _base_address+(size*nof_instances), size * MM_BUS_SIZE, name, nof_instances, str(hide_in_reg_file))
 
     def _find_start_addr(self, size):
         """ look for the next address where the size (in bytes) will fit """