diff --git a/gen_doc.py b/gen_doc.py
index 874eaba6afce0d85f462dace732796a9b18579e3..00399aba24af0122dbab153474618342219015d4 100755
--- a/gen_doc.py
+++ b/gen_doc.py
@@ -27,6 +27,11 @@
 """
 Make automatic documentation
 
+Usage:
+> gen_doc.py -h
+> gen_doc.py -f lofar2_unb2b_beamformer -v DEBUG
+> gen_doc.py -f unb2b_minimal
+> gen_doc.py -f lofar2_unb2b_beamformer
 """
 
 import sys
diff --git a/gen_rom_mmap.py b/gen_rom_mmap.py
index 256d25ce1a65096c5dcd3bb3d099f6e6e0677277..d3a6c823ea62ecc67d765b4fc90ea118521c66d7 100755
--- a/gen_rom_mmap.py
+++ b/gen_rom_mmap.py
@@ -26,6 +26,12 @@
 """
     Generate fpgamap file to copy in rom_system  [fpga_name].mmap
     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
 """
 
 import sys
@@ -37,12 +43,19 @@ from py_args_lib import FPGA, RAM, FIFO, Register, PeripheralLibrary, FPGALibrar
 import pprint
 
 
-def make_mask(width, offset=0):
-    _hi_bit = offset + width - 1
-    _lo_bit = offset
-    # use [hi:lo] format also for [i:i] = [i], to ease parsing because the bit mask then always contain a :
-    _mask_str = 'b[{}:{}]'.format(_hi_bit, _lo_bit)
-    return _mask_str
+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
+    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
 
 
 def gen_fpga_map(fpga, fpga_name):
@@ -51,7 +64,7 @@ def gen_fpga_map(fpga, fpga_name):
     # pprint.pprint(fpga.address_map)
     print("Including slave ports for {}:".format(fpga_name))
     map_nof_columns = 9
-    map_format_str = '  {:24s}  {:4s}  {:5s}  {:24s}  0x{:08x}  {:6d}  {:>5s}  {:>11s}  {:>10s}'
+    map_format_str = '  {:24s}  {:4s}  {:4s}  {:5s}  {:40s}  0x{:08x}  {:6d}  {:>5s}  {:>11s}  {:>5s}  {:>10s}'
     for slave_port_name, slave_port_info in fpga.address_map.items():
 
         # All elements in array have same info, so only need info from first element
@@ -59,50 +72,71 @@ def gen_fpga_map(fpga, fpga_name):
             continue
 
         #print("slave_port_info = {}".format(slave_port_info))
-        
         slave            = slave_port_info['slave']
         user_def_name    = slave.user_defined_name().upper()
+
+        #print("number_of_peripherals = {}".format(fpga.peripherals))
+        number_of_peripherals = str(1)  # EK TODO: get number_of_peripherals from the peripheral instance in fpga.yaml
+        if user_def_name in ('RAM_SS_SS_WIDE', 'RAM_BF_WEIGHTS', 'REG_BF_SCALE', 'REG_HDR_DAT', 'REG_DP_XONOFF', 'RAM_ST_BST'):
+            number_of_peripherals = str(2)   # EK: FIXME temporary work around
+
         number_of_slaves = str(slave.number_of_slaves())
         base             = int(slave_port_info['base'])
         base_word        = int(base / WORD_SIZE)
 
         # EK TODO: new structured naming: slave_port_name = slave_name + peripheral_group ~= user_def_name
         if isinstance(slave, RAM):
-            print(' RAM  {:50s} at 0x{:08x}(bytes) 0x{:04x}(words)  "{}"'.format(slave_port_name, base, base_word, user_def_name))
+            print(' RAM  {:80s} at 0x{:08x}(bytes) 0x{:04x}(words)  "{}"'.format(slave_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 = '-' 
+            
             _map_str.append(map_format_str.format(
                             user_def_name,
+                            number_of_peripherals,
                             number_of_slaves,
                             'RAM',
                             'data',   # EK: TODO should come from yaml field_name
                             base_word,
                             slave.number_of_fields(),
                             slave.access_mode(),
-                            '-',  # EK: TODO should get radix from field
-                            '-'  # EK: TODO should use make_mask() to report field_width, bit_offset of RAM data
+                            radix,
+                            radix_packing,
+                            radix_bit_mask
                             ))
 
         elif isinstance(slave, FIFO):
-            print(' FIFO {:50s} at 0x{:08x}(bytes) 0x{:04x}(words)  "{}"'.format(slave_port_name, base, base_word, user_def_name))
+            print(' FIFO {:80s} at 0x{:08x}(bytes) 0x{:04x}(words)  "{}"'.format(slave_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 = '-' 
+            
             _map_str.append(map_format_str.format(
                             user_def_name,
+                            number_of_peripherals,
                             number_of_slaves,
                             'FIFO',
                             'data',   # EK: TODO should come from yaml field_name
                             base_word,
                             slave.number_of_fields(),
                             slave.access_mode(),
-                            '-',  # EK: TODO should get radix from field
-                            '-'  # EK: TODO should use make_mask() to report field_width, bit_offset of FIFO data, there seems no slave.fields
+                            radix,
+                            radix_packing,
+                            radix_bit_mask
                             ))
 
         elif isinstance(slave, Register):
             slave_type       = "REG"   # EK: TODO get slave_type from slave ?
-            print(' REG  {:50s} at 0x{:08x}(bytes) 0x{:04x}(words)  "{}"'.format(slave_port_name, base, base_word, user_def_name))
+            print(' REG  {:80s} at 0x{:08x}(bytes) 0x{:04x}(words)  "{}"'.format(slave_port_name, base, base_word, user_def_name))
             done = []
             for f in slave.fields:
                 field_name = f.name()
                 
-                #print(f.radix().lower())  # EK: TODO do not use radix in mmap ?
+                #print('{}: {}, {}, {}'.format(field_name, f.radix().lower(), f.radix_width(), f.radix_resolution()))
                 
                 # 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 slave.fields. The slave.number_of_fields() should not have been expanded in slave.
                 if f.number_of_fields() > 1:
@@ -112,14 +146,19 @@ def gen_fpga_map(fpga, fpga_name):
                 if field_name not in done:
                     done.append(field_name)
                     
-                field_group = f.group_name()
-                if field_group == None:
-                    field_group = '-'
+                #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())
 
                 _map_str.append(map_format_str.format(
                                 user_def_name,
+                                number_of_peripherals,
                                 number_of_slaves,
                                 slave_type,
                                 field_name,
@@ -127,11 +166,13 @@ def gen_fpga_map(fpga, fpga_name):
                                 f.number_of_fields(),
                                 f.access_mode(),
                                 f.radix(),
-                                make_mask(f.width(), f.bit_offset())
+                                radix_packing,
+                                radix_bit_mask
                                 ))
                 
                 # only log table entry for first field of slave
                 user_def_name = '-'
+                number_of_peripherals = '-'
                 number_of_slaves = '-'
                 slave_type = '-'
 
@@ -139,17 +180,19 @@ def gen_fpga_map(fpga, fpga_name):
     _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: slave_port_name  # = user defined QSYS name,  if - then it is part of previous slave port.')
-    _map_info.append('# col 2: number of slaves, if - then it is part of previous slave.')
-    _map_info.append('# col 3: slave_type, if - then it is part of previous slave.')
-    _map_info.append('# col 4: field_name')
-    _map_info.append('# col 5: field start address (in words)')
-    _map_info.append('# col 6: field address span = number of fields (in words)')
-    _map_info.append('# col 7: field access_mode')
-    _map_info.append('# col 8: field radix')
-    _map_info.append('# col 9: field bit mask')
+    _map_info.append('# col 2: number of peripherals, if - then it is part of previous peripheral.')
+    _map_info.append('# col 3: number of slaves, if - then it is part of previous slave.')
+    _map_info.append('# col 4: slave_type, if - then it is part of previous slave.')
+    _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('#')
-    _map_info.append('# col1                      col2  col3   col4                      col5        col6    col7   col8         col9')
-    _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/py_args_lib/peripheral.py b/py_args_lib/peripheral.py
index 6f6b174798922f128cd01db2ae622b2cb25eaa73..88dee035e707c5d16b8139fdf05a88c302784c0a 100644
--- a/py_args_lib/peripheral.py
+++ b/py_args_lib/peripheral.py
@@ -341,7 +341,7 @@ class Peripheral(BaseObject):
         register.number_of_slaves(number_of_slaves)
         register.isIP = isIP
         register.slave_span = slave_span
-        if protocol is not None and protocol.upper() in ['LITE', 'FULL']:
+        if protocol is not None and protocol.upper() in ['LITE', 'FULL']:   # EK: TODO Remove AXI specific protocol field
             register.protocol = protocol.upper()
         self.registers['slave_{}'.format(slave_nr)] = register
         self.slaves.append(register)
diff --git a/py_args_lib/peripheral_lib/base_object.py b/py_args_lib/peripheral_lib/base_object.py
index b25841111cc4491ddd881170f1ef98b3ed2864ad..ea9fcd485c084f2c61a7c37c596a84ed701d2c87 100644
--- a/py_args_lib/peripheral_lib/base_object.py
+++ b/py_args_lib/peripheral_lib/base_object.py
@@ -31,6 +31,14 @@ from args_errors import ARGSNameError
 
 logger = logging.getLogger('main.base_object')
 
+# EK: FIXME support definition and passing on of negative parameter values, workaround:
+#           . use value: 0 - 15 instead of value: -15
+#           . use value: 0 - c_K instead of value: -c_K
+#     alternatively:
+#           . define negate(x) --> -x function in common_func.py
+#           . define parameter constant c_minus_one = 0 - 1 in fpga.yaml and use c_minus_one * x --> -x
+#     ==> I think it is fine to use one f these alternatives for now, because it may be difficult
+#         to parse the yaml file and distinguish between - = minus and - is list element.
 
 class BaseObject(object):
     def __init__(self):
@@ -128,7 +136,7 @@ class BaseObject(object):
         return self._user_defined_name
 
     def prefix(self, val=None):
-        """ set/get prefix """
+        """ set/get prefix """   # EK: TODO this prefix method can probably be removed, because no more need for structured naming.
         if val is not None:
             self._prefix = val
             return
diff --git a/py_args_lib/peripheral_lib/common_func.py b/py_args_lib/peripheral_lib/common_func.py
index fe2f19f7c8842fef12c76d26056b291fbb7e9ddd..3c981e6d747327913173d9c5eb267bd8bd5d1f09 100644
--- a/py_args_lib/peripheral_lib/common_func.py
+++ b/py_args_lib/peripheral_lib/common_func.py
@@ -40,6 +40,14 @@ def ceil_pow2(num):
     return 2**ceil_log2(num)
 
     
+def sel_a_b(sel, a, b):
+    """ Return a when sel = True, else return b """
+    if sel:
+        return a
+    else:
+        return b
+
+
 def unique(in_list):
     """
     Extract unique list elements (without changing the order like set() does)
diff --git a/py_args_lib/peripheral_lib/constants.py b/py_args_lib/peripheral_lib/constants.py
index db2c119d03722dd338d80d9aee33ca0da28a4c35..298dfae436995dad89bf3fd93a27d35e3c092917 100644
--- a/py_args_lib/peripheral_lib/constants.py
+++ b/py_args_lib/peripheral_lib/constants.py
@@ -35,6 +35,7 @@ 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
@@ -58,7 +59,10 @@ VALID_FIELD_KEYS = ['field_description',
                     'number_of_fields',
                     'reset_value',
                     'software_value',
-                    'radix']    # EK: TODO field_name is missing in this list ? Use same order as in ARGS doc.
+                    '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?
 
 VALID_DEFAULT_FIELD_KEYS = ['field_description',
                             'width',
@@ -68,7 +72,7 @@ VALID_DEFAULT_FIELD_KEYS = ['field_description',
                             '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', 'complex']
+VALID_FIELD_RADIXS = ['unsigned', 'signed', 'hexadecimal', 'char', 'complx']  # EK: TODO change complx into complex, but complex gets misinterpreted by Python
 
 
 UNASSIGNED_ADDRESS = 16384     # EK: TODO why is this needed ? Should be -1 for undefined, or just default 0 ?
@@ -89,6 +93,8 @@ 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_DESCRIPTION      = 'NONE'
 
 DATA_WIDTH               = 32   # EK: TODO purpose ? use WORD_WIDTH instead ?
diff --git a/py_args_lib/peripheral_lib/field.py b/py_args_lib/peripheral_lib/field.py
index c44abee0980d43e428bd82cf5b660cba1252fd50..f24cba99d1af4fc1ab82a6a7b34604318c449fc2 100644
--- a/py_args_lib/peripheral_lib/field.py
+++ b/py_args_lib/peripheral_lib/field.py
@@ -44,16 +44,18 @@ class Field(BaseObject):
 
         self._valid_dict = {'field_name'        : {},
                             'field_description' : {},
-                            'width'             : {'max': 32, 'min': 1},
+                            '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},
                             'bit_offset'        : {'max': UNASSIGNED_BIT, 'min': 0},
                             'access_mode'       : {},
                             'side_effect'       : {},
-                            'address_offset'    : {'max': 16384, 'min': 0, 'word_aligned': True},
+                            'address_offset'    : {'max': 262144, 'min': 0, 'word_aligned': True},
                             'number_of_fields'  : {'max': 262144, 'min': 1},
                             'reset_value'       : {'max': 131071},
                             'software_value'    : {},
-                            'radix'             : {}}
+                            'radix'             : {},
+                            'radix_width'       : {},
+                            'radix_resolution'  : {}}
 
         self._args.update({'field_description': DEFAULT_FIELD_DESCRIPTION,
                            'width'            : DEFAULT_FIELD_WIDTH,
@@ -65,6 +67,8 @@ class Field(BaseObject):
                            '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,
                            'group_name'       : None})
 
         if settings is not None:
@@ -191,6 +195,7 @@ class Field(BaseObject):
         val: if not None set default value of field
         return: active radix value of field """
         if val is not None:
+            #print(val)
             if val.lower() in VALID_FIELD_RADIXS:
                 return self.set_kv('radix', val.lower())
             else:
@@ -199,6 +204,26 @@ 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
+        val: if not None set default value of field
+        return: active radix_resolution value of field """
+        if val is not None:
+            return self.set_kv('radix_resolution', val)
+        if not self.is_valid('radix_resolution'):
+            return None
+        return self._as_int('radix_resolution')
+
     def field_description(self, val=None):
         """ set/get description of field
         val: if not None set description of field
diff --git a/py_args_lib/peripheral_lib/register.py b/py_args_lib/peripheral_lib/register.py
index 211573df2ec4816a7f1c81e74d88df818ddf8bd4..94148573389e07d11d101992911fb819a01ca646 100644
--- a/py_args_lib/peripheral_lib/register.py
+++ b/py_args_lib/peripheral_lib/register.py
@@ -35,7 +35,7 @@ from field import Field
 logger = logging.getLogger('main.periph.register')
 
 
-class Register(BaseObject):
+class Register(BaseObject):    # EK: TODO rename to REG(), to match RAM and FIFO class names and to match slave_type key in peripheral.yaml
     """ A register consists of Fields
     """
     def __init__(self, name, fields=None, settings=None):
@@ -46,20 +46,20 @@ class Register(BaseObject):
 
         self.fields = [] if fields is None else fields
 
-        self.rams = []
+        self.rams = []   # EK: TODO remove rams, because no rams in Register
 
-        self._valid_keys = ['number_of_slaves', 'address_length', 'slave_description', 'base_address']
+        self._valid_keys = ['number_of_slaves', 'address_length', 'slave_description', 'base_address']  # EK: TODO why here and not in constants.py, best use only one place
 
         self._args.update({'number_of_slaves' : DEFAULT_NUMBER_OF_SLAVES,
-                           'address_length'   : DEFAULT_ADDRESS_LENGTH,
+                           'address_length'   : DEFAULT_ADDRESS_LENGTH,          # EK: TODO strange address_length is not an ARGS yaml key, so why in this list?
                            'dual_clock'       : False,
-                           'slave_description': DEFAULT_SLAVE_DESCRIPTION})
+                           'slave_description': DEFAULT_SLAVE_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.slave_span = None
 
-        self.protocol = 'LITE'   # EK: TODO remove AXI specific protocol field
+        self.protocol = 'LITE'   # EK: TODO remove AXI specific protocol field (LITE, FULL)
 
     def number_of_slaves(self, val=None):
         """ set/get number of slaves """
diff --git a/slave_port_naming.txt b/slave_port_naming.txt
index 7e1a73d02e072cc95ec227a479de50c850503cf2..f52542702ac413b1b08e790e05afc0c0f6d4a25e 100644
--- a/slave_port_naming.txt
+++ b/slave_port_naming.txt
@@ -124,10 +124,8 @@
 
 
 4) Summary
-a) Remove number_of_peripherals, so effectively only support number_of_peripherals = 1, because:
-   - this is fine for exising QSYS MM bus designs
-   - multiple periperal instances can still be supported using different periperal_group labels. The periperal_group label can also be an index, like used with QSYS. 
-   - having only the number_of_slaves array level is simpler and sufficient
+a) Keep number_of_peripherals in fpga.yaml, because this second array level is needed when the
+   fpga design uses an array of peripherals, like with N_beamsets = 2 in SDP FW.
 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
@@ -145,14 +143,25 @@ e) field
    - do not use field_group as part of field_name, instead keep them separate --> of toch wel nuttig zie system info in unb2b_board.periperal.yaml? 
      . without field_group string ARGS still adds a prefix reg0_ in case of multiple fields per word --> niet goed
    - rename width to field_width to more clearly recognize it and distinguish it from user_width, so field.width --> field.field_width ?
+   - support field_group > 32b and number_of_field_groups to allow repeating field structures?
+   - field_group
+     . no need to support field_group > 32b, because 32b is sufficient, easier and already supported by ARGS.
+     . no need to support number_of_field_groups > 1 to define a recurrent substructure in a REG or RAM, because
+       this kind of array represents only one extra array level [], but for e.g. RAM we would then want multi
+       array level e.g. [] or [][] or [][][], etc, and number_of_field_groups is not yet supported by ARGS.
+   - future support multi array level using new radix_dimensions ARGS key
+     . instead of number_of_field_groups it may be useful to support multi array level by a radix_dimensions tuple that
+       gives the dimensions of the array that fits in the RAM, e.g. radix_dimensions: (3,4,2) for array [3][4][2] of
+       elements radix type and radix_width.
    - radix:
      . use radix u1, u8, i16, ch8, f32, d64 etc to indicate element type and width.
-        - radix_type
+        - radix_type = radix
         - radix_width
         - radix_resolution, default 0 for unit = 2**radix_resolution = 1 or not used, can be > 0 or < 0,
               The purpose of the radix resolution is to convert between float and fixed point. However if the 
               fixed point value is passed on transparently then the radix resolution is not needed in the mmap.
      . fields can overlap, this allows accessing multiple fields at once or per field.
+
      . endianess
        - defined upto 8 byte data (64b), e.g.:
            int64         0x0706050403020100
@@ -163,6 +172,8 @@ e) field
        - the endianess is not changed by the M&C, it is passed on as it is --> therefore there is no need for an endianess key or column in mmap.
      . add radix columns to mmap? --> yes:
        . width, bit_offset, radix_width --> field bit mask [hi:lo]
+           if radix_width is not specified or 0, then radix_width = width, so radix_width can always be
+           used to determine the bit mask.
        . radix_resolution --> field resolution
      . add more radix types:
                         width     
@@ -174,24 +185,67 @@ e) field
        - d, double    =   64b
        - u, unsigned  >=   1b
        - s, signed    >=   1b
-       - c, complex   >= 2*1b --> packed signed im,re in width, so give ERROR when width % 2 != 0
-       
-     . maximum width = 32 is the MM width.
-       - allow radix_width < field_width to pack smaller elements into width. Give ERROR when (field_width % radix_width) != 0
-       - allow radix_width > field_width to support larger elements e.g. 48b,  64b. The field is defined in the first
-             32b word and extends into the next 32b word(s).
+       - c, complex   >= 2*1b --> packed signed im,re in width, so give ERROR when width % 2 != 0,
+                                  . so define width for the pair of im and re, no for only the width
+                                    of the im, re, because width, or radix_width, define the total number of bits.
+                                  . so pack first re at lowest part then hi at highest part, kind of
+                                    little endian order (i.e. re first) for complex values.
        
+     . complex value packing:
+       - if radix_width == width <= 32 then packed signed re in bit mask[width/2-1:0]
+                                                          im in bit mask[width-1:width/2]
+       - if radix_width  > width == 32 then packed signed re in bit mask[radix_width/2-1:0]
+                                                          im in bit mask[radix_width-1:radix_width/2]
+            For example a complex value with radix_width = 48 is stored as:
+                word  byte
+                addr  addr  bits
+                  0   0x0  [23:0]  = re[23:0]
+                      0x0  [31:24] = im[7:0]
+                  1   0x4  [15:0]  = im[23:8]
+            With only the bit_mask in the mmap it is not possible to define the re and im at separate words using radix: complex
+            A complex value with radix_width = 64 will be stored at separate words:
+                word  byte
+                addr  addr  bits
+                  0   0x0  [31:0] = re[31:0]
+                  1   0x4  [31:0] = im[31:0]
+            Using signed instead of complex it is possible to define the complex values with re, im in separate words using
+            width = 24:
+                word  byte
+                addr  addr  bits
+                  0   0x0  [23:0] = re[23:0]
+                  0   0x0  [23:0] = im[23:0]
+            However then it is not clear from the radix type that the fields are in fact the parts of complex values.
+
+
+     . maximum field width = 32 is the MM width.
+       - allow radix_width < field width to pack smaller elements into width. Give ERROR when (field_width % radix_width) != 0
+       - allow radix_width > field width to support larger elements e.g. 48b,  64b. The field is defined in the first
+             32b word and extends into lsbits of the next 32b word(s), a value with radix_width = 48 is stored as:
+                word  byte
+                addr  addr  bits
+                  0   0x0  [31:0] = value[31:0]
+                  1   0x4  [15:0] = value[47:32]
+             so this is radix_width > width is treated as little endian, because lowest byte is at lowest byte address.
+
      . support packing array of fields dependent on radix_width and field_width:
        - keep maximum field_width = 1..32
        - support packed array by using field_width = n * radix_width, so for string of char e.g. use field_width = 32 and number_of_fields 13 to have n = 52 char.
        - support array with one field per address by using field_wdith = radix_width
        - the radix array size depends on:
          . radix_array_size = number_of_fields * width / radix_width
-         
+
+     . bit_mask
+       . when radix_width < width <= 32 --> bit_mask tells element width and radix_packing tells how many elements are packed per 32b field
+       . when radix_width > width --> ignore width, the bit_mask uses the radix_width. If radix_width > 32b then it extends into the next
+         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:
-     . RAM and FIFO only have one field in fields, REG can have multiple fields
-     . All can have field_group, although only useful for REG
+     . 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.
+     . All can have field_group, can also be useful for FIFO and RAM, because
+       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.
@@ -212,7 +266,7 @@ h) mmap
    - only base address and span in mmap or do also we need the fields in mmap?
    - store spaces ' ' as single space in mmap to reduce size in FPGA?
    - [i:i] or [i] ?
-   - also use [] for ram and fifo
+   - also use bit_mask [:] for ram and fifo
    - add some key value pairs at start of mmap:
      . fpga_name = fpga_name of FPGA design for reference to the fpga.yaml file. Only for reference, because the
            mmap is self contained, so no need to access the yaml file
@@ -221,8 +275,7 @@ h) mmap
    - only non-redundant info in mmap, not needed now:
      . hdl_library_name,
        peripheral_name,
-       peripheral_group,
-       field_group, because the slave port name uniquely defines the MM port, no need to know its related origin keys.
+       peripheral_group, because the slave 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
      . endianness, because it values are passed on transparently, mmap does not change the endianess
@@ -248,16 +301,75 @@ h) mmap
          - - field_name: bsn_init_hi
              field_description: "Initial BSN[63:32] hi part of 64b value"
              address_offset: 0xC
-       
-     . radix resolution, because values are transparently passed on
+       Note: When radix_width > field width + bit_offset, then the field width is don't care because then the radix
+             extends into the next field 32b word. Hence then choose field width such (e.g. 1) that field width +
+             bit_offset <= 32b, which is the maximum field width.
+     . radix_array_length, if < nof_radix_per_field * number_of_fields, e.g. for a string of 50 char packed in
+           nof_radix_per_field = 4 char per field_width = 32b word, so with number_of_fields = 13, there are
+           with 2 bytes unused, because 13 * 4 = 52.
+     . radix resolution, because values are transparently passed on --> except if the have to be translated
+         from e.g. float to fixed point or scaled in the translator, however then more settings may be needed
+         like e.g. an offset value (for converting temperature integer to celcius).
+     . address spans are already incorporated per field into the field start address and field address span,
+       so no need in mmap for:
+       - peripheral base address is covered by address of first field.
+         . The peripheral base address can be forced by lock_base_address and has to be on a multiple of:
+             2**ceil_log2(number_of_peripherals * 2**ceil_log2(peripheral_span))
+           to allow local address decoding within the peripheral
+         . For minimal address map locations usage the fpga address map should start with the smallest
+           peripherals (array) spans first.
+         . 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
+                           #   width, bit_offset, radix_width.
+                           # . Keep slave_span key to enforce a it from peripheral.yaml in case the span
+                           #   needs to be larger than needed.
    - already existing columns:
-     . field radix type    # = radix
-     . field bit mask      # derived from width, bit_offset, radix_width
-     
- 
-   
+     . 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.
+
+
+i) 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)
+  - 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)
+    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
+
 z) Other:
-   - Rename Register.py into reg.py and REG.
+  - 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