Skip to content
Snippets Groups Projects
Commit e2c12083 authored by Eric Kooistra's avatar Eric Kooistra
Browse files

Merge branch 'L2SDP-240' into 'master'

L2 sdp 240

See merge request desp/args!3
parents 26240f3d 4b56209d
Branches
No related tags found
1 merge request!3L2 sdp 240
...@@ -27,6 +27,11 @@ ...@@ -27,6 +27,11 @@
""" """
Make automatic documentation 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 import sys
......
...@@ -26,6 +26,12 @@ ...@@ -26,6 +26,12 @@
""" """
Generate fpgamap file to copy in rom_system [fpga_name].mmap 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
""" """
import sys import sys
...@@ -37,12 +43,19 @@ from py_args_lib import FPGA, RAM, FIFO, Register, PeripheralLibrary, FPGALibrar ...@@ -37,12 +43,19 @@ from py_args_lib import FPGA, RAM, FIFO, Register, PeripheralLibrary, FPGALibrar
import pprint import pprint
def make_mask(width, offset=0): def _make_mask(field_width, radix_width=0, bit_offset=0):
_hi_bit = offset + width - 1 """Derive radix bit mask and number of radix elements per field."""
_lo_bit = offset _nof_radix_per_field = 1
# use [hi:lo] format also for [i:i] = [i], to ease parsing because the bit mask then always contain a : if radix_width == 0 or radix_width == field_width:
_mask_str = 'b[{}:{}]'.format(_hi_bit, _lo_bit) _hi_bit = bit_offset + field_width - 1
return _mask_str 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): def gen_fpga_map(fpga, fpga_name):
...@@ -51,7 +64,7 @@ def gen_fpga_map(fpga, fpga_name): ...@@ -51,7 +64,7 @@ def gen_fpga_map(fpga, fpga_name):
# pprint.pprint(fpga.address_map) # pprint.pprint(fpga.address_map)
print("Including slave ports for {}:".format(fpga_name)) print("Including slave ports for {}:".format(fpga_name))
map_nof_columns = 9 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(): 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 # 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): ...@@ -59,50 +72,71 @@ def gen_fpga_map(fpga, fpga_name):
continue continue
#print("slave_port_info = {}".format(slave_port_info)) #print("slave_port_info = {}".format(slave_port_info))
slave = slave_port_info['slave'] slave = slave_port_info['slave']
user_def_name = slave.user_defined_name().upper() 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()) number_of_slaves = str(slave.number_of_slaves())
base = int(slave_port_info['base']) base = int(slave_port_info['base'])
base_word = int(base / WORD_SIZE) base_word = int(base / WORD_SIZE)
# EK TODO: new structured naming: slave_port_name = slave_name + peripheral_group ~= user_def_name # EK TODO: new structured naming: slave_port_name = slave_name + peripheral_group ~= user_def_name
if isinstance(slave, RAM): 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( _map_str.append(map_format_str.format(
user_def_name, user_def_name,
number_of_peripherals,
number_of_slaves, number_of_slaves,
'RAM', 'RAM',
'data', # EK: TODO should come from yaml field_name 'data', # EK: TODO should come from yaml field_name
base_word, base_word,
slave.number_of_fields(), slave.number_of_fields(),
slave.access_mode(), slave.access_mode(),
'-', # EK: TODO should get radix from field radix,
'-' # EK: TODO should use make_mask() to report field_width, bit_offset of RAM data radix_packing,
radix_bit_mask
)) ))
elif isinstance(slave, FIFO): 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( _map_str.append(map_format_str.format(
user_def_name, user_def_name,
number_of_peripherals,
number_of_slaves, number_of_slaves,
'FIFO', 'FIFO',
'data', # EK: TODO should come from yaml field_name 'data', # EK: TODO should come from yaml field_name
base_word, base_word,
slave.number_of_fields(), slave.number_of_fields(),
slave.access_mode(), slave.access_mode(),
'-', # EK: TODO should get radix from field radix,
'-' # EK: TODO should use make_mask() to report field_width, bit_offset of FIFO data, there seems no slave.fields radix_packing,
radix_bit_mask
)) ))
elif isinstance(slave, Register): elif isinstance(slave, Register):
slave_type = "REG" # EK: TODO get slave_type from slave ? 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 = [] done = []
for f in slave.fields: for f in slave.fields:
field_name = f.name() 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. # 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: if f.number_of_fields() > 1:
...@@ -112,14 +146,19 @@ def gen_fpga_map(fpga, fpga_name): ...@@ -112,14 +146,19 @@ def gen_fpga_map(fpga, fpga_name):
if field_name not in done: if field_name not in done:
done.append(field_name) done.append(field_name)
field_group = f.group_name() #field_group = f.group_name()
if field_group == None: #if field_group == None:
field_group = '-' # field_group = '-'
f_base = base_word + int(f.address_offset() / WORD_SIZE) 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( _map_str.append(map_format_str.format(
user_def_name, user_def_name,
number_of_peripherals,
number_of_slaves, number_of_slaves,
slave_type, slave_type,
field_name, field_name,
...@@ -127,11 +166,13 @@ def gen_fpga_map(fpga, fpga_name): ...@@ -127,11 +166,13 @@ def gen_fpga_map(fpga, fpga_name):
f.number_of_fields(), f.number_of_fields(),
f.access_mode(), f.access_mode(),
f.radix(), f.radix(),
make_mask(f.width(), f.bit_offset()) radix_packing,
radix_bit_mask
)) ))
# only log table entry for first field of slave # only log table entry for first field of slave
user_def_name = '-' user_def_name = '-'
number_of_peripherals = '-'
number_of_slaves = '-' number_of_slaves = '-'
slave_type = '-' slave_type = '-'
...@@ -139,17 +180,19 @@ def gen_fpga_map(fpga, fpga_name): ...@@ -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('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('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 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 2: number of peripherals, if - then it is part of previous peripheral.')
_map_info.append('# col 3: slave_type, if - then it is part of previous slave.') _map_info.append('# col 3: number of slaves, if - then it is part of previous slave.')
_map_info.append('# col 4: field_name') _map_info.append('# col 4: slave_type, if - then it is part of previous slave.')
_map_info.append('# col 5: field start address (in words)') _map_info.append('# col 5: field_name')
_map_info.append('# col 6: field address span = number of fields (in words)') _map_info.append('# col 6: field start address (in words)')
_map_info.append('# col 7: field access_mode') _map_info.append('# col 7: field address span (in words)')
_map_info.append('# col 8: field radix') _map_info.append('# col 8: field access_mode')
_map_info.append('# col 9: field bit mask') _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('#')
_map_info.append('# col1 col2 col3 col4 col5 col6 col7 col8 col9') _map_info.append('# col1 col2 col3 col4 col5 col6 col7 col8 col9 col10 col11')
_map_info.append('# ------------------------ ---- ----- ------------------------ ---------- ------ ----- ----------- ----------') _map_info.append('# ------------------------ ---- ---- ----- ---------------------------------------- ---------- ------ ----- ----------- ----- ----------')
out_dir = os.path.join(os.getenv('ARGS_BUILD_DIR'), fpga.board_name.replace('uniboard','unb'), 'args', fpga_name, 'c') out_dir = os.path.join(os.getenv('ARGS_BUILD_DIR'), fpga.board_name.replace('uniboard','unb'), 'args', fpga_name, 'c')
try: try:
......
...@@ -341,7 +341,7 @@ class Peripheral(BaseObject): ...@@ -341,7 +341,7 @@ class Peripheral(BaseObject):
register.number_of_slaves(number_of_slaves) register.number_of_slaves(number_of_slaves)
register.isIP = isIP register.isIP = isIP
register.slave_span = slave_span 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() register.protocol = protocol.upper()
self.registers['slave_{}'.format(slave_nr)] = register self.registers['slave_{}'.format(slave_nr)] = register
self.slaves.append(register) self.slaves.append(register)
......
...@@ -31,6 +31,14 @@ from args_errors import ARGSNameError ...@@ -31,6 +31,14 @@ from args_errors import ARGSNameError
logger = logging.getLogger('main.base_object') 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): class BaseObject(object):
def __init__(self): def __init__(self):
...@@ -128,7 +136,7 @@ class BaseObject(object): ...@@ -128,7 +136,7 @@ class BaseObject(object):
return self._user_defined_name return self._user_defined_name
def prefix(self, val=None): 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: if val is not None:
self._prefix = val self._prefix = val
return return
......
...@@ -40,6 +40,14 @@ def ceil_pow2(num): ...@@ -40,6 +40,14 @@ def ceil_pow2(num):
return 2**ceil_log2(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): def unique(in_list):
""" """
Extract unique list elements (without changing the order like set() does) Extract unique list elements (without changing the order like set() does)
......
...@@ -35,6 +35,7 @@ c_nof_complex = 2 ...@@ -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. # 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. # 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 # 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_WIDTH = c_word_w # = 32 bits
WORD_SIZE = c_word_w // c_byte_w # = 4 bytes WORD_SIZE = c_word_w // c_byte_w # = 4 bytes
...@@ -58,7 +59,10 @@ VALID_FIELD_KEYS = ['field_description', ...@@ -58,7 +59,10 @@ VALID_FIELD_KEYS = ['field_description',
'number_of_fields', 'number_of_fields',
'reset_value', 'reset_value',
'software_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', VALID_DEFAULT_FIELD_KEYS = ['field_description',
'width', 'width',
...@@ -68,7 +72,7 @@ VALID_DEFAULT_FIELD_KEYS = ['field_description', ...@@ -68,7 +72,7 @@ VALID_DEFAULT_FIELD_KEYS = ['field_description',
'number_of_fields', 'number_of_fields',
'reset_value'] # EK: TODO change to MANDATORY_FIELD_KEYS to have smaller list ? Current list is incomplete. '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 ? 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 ...@@ -89,6 +93,8 @@ DEFAULT_FIELD_NUMBER_OF_FIELDS = 1
DEFAULT_FIELD_RESET_VALUE = 0 DEFAULT_FIELD_RESET_VALUE = 0
DEFAULT_FIELD_SOFTWARE_VALUE = 0 DEFAULT_FIELD_SOFTWARE_VALUE = 0
DEFAULT_FIELD_RADIX = 'unsigned' 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' DEFAULT_FIELD_DESCRIPTION = 'NONE'
DATA_WIDTH = 32 # EK: TODO purpose ? use WORD_WIDTH instead ? DATA_WIDTH = 32 # EK: TODO purpose ? use WORD_WIDTH instead ?
......
...@@ -44,16 +44,18 @@ class Field(BaseObject): ...@@ -44,16 +44,18 @@ class Field(BaseObject):
self._valid_dict = {'field_name' : {}, self._valid_dict = {'field_name' : {},
'field_description' : {}, '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}, 'user_width' : {'max': 2048, 'min': 32},
'bit_offset' : {'max': UNASSIGNED_BIT, 'min': 0}, 'bit_offset' : {'max': UNASSIGNED_BIT, 'min': 0},
'access_mode' : {}, 'access_mode' : {},
'side_effect' : {}, '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}, 'number_of_fields' : {'max': 262144, 'min': 1},
'reset_value' : {'max': 131071}, 'reset_value' : {'max': 131071},
'software_value' : {}, 'software_value' : {},
'radix' : {}} 'radix' : {},
'radix_width' : {},
'radix_resolution' : {}}
self._args.update({'field_description': DEFAULT_FIELD_DESCRIPTION, self._args.update({'field_description': DEFAULT_FIELD_DESCRIPTION,
'width' : DEFAULT_FIELD_WIDTH, 'width' : DEFAULT_FIELD_WIDTH,
...@@ -65,6 +67,8 @@ class Field(BaseObject): ...@@ -65,6 +67,8 @@ class Field(BaseObject):
'reset_value' : DEFAULT_FIELD_RESET_VALUE, 'reset_value' : DEFAULT_FIELD_RESET_VALUE,
'software_value' : DEFAULT_FIELD_SOFTWARE_VALUE, 'software_value' : DEFAULT_FIELD_SOFTWARE_VALUE,
'radix' : DEFAULT_FIELD_RADIX, 'radix' : DEFAULT_FIELD_RADIX,
'radix_width' : DEFAULT_FIELD_RADIX_WIDTH,
'radix_resolution' : DEFAULT_FIELD_RADIX_RESOLUTION,
'group_name' : None}) 'group_name' : None})
if settings is not None: if settings is not None:
...@@ -191,6 +195,7 @@ class Field(BaseObject): ...@@ -191,6 +195,7 @@ class Field(BaseObject):
val: if not None set default value of field val: if not None set default value of field
return: active radix value of field """ return: active radix value of field """
if val is not None: if val is not None:
#print(val)
if val.lower() in VALID_FIELD_RADIXS: if val.lower() in VALID_FIELD_RADIXS:
return self.set_kv('radix', val.lower()) return self.set_kv('radix', val.lower())
else: else:
...@@ -199,6 +204,26 @@ class Field(BaseObject): ...@@ -199,6 +204,26 @@ class Field(BaseObject):
return False return False
return self._as_int('radix') 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): def field_description(self, val=None):
""" set/get description of field """ set/get description of field
val: if not None set description of field val: if not None set description of field
......
...@@ -35,7 +35,7 @@ from field import Field ...@@ -35,7 +35,7 @@ from field import Field
logger = logging.getLogger('main.periph.register') 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 """ A register consists of Fields
""" """
def __init__(self, name, fields=None, settings=None): def __init__(self, name, fields=None, settings=None):
...@@ -46,20 +46,20 @@ class Register(BaseObject): ...@@ -46,20 +46,20 @@ class Register(BaseObject):
self.fields = [] if fields is None else fields 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, 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, '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.update_address_length()
self.isIP = False # EK: TODO remove platform specific isIP self.isIP = False # EK: TODO remove platform specific isIP
self.slave_span = None 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): def number_of_slaves(self, val=None):
""" set/get number of slaves """ """ set/get number of slaves """
......
...@@ -124,10 +124,8 @@ ...@@ -124,10 +124,8 @@
4) Summary 4) Summary
a) Remove number_of_peripherals, so effectively only support number_of_peripherals = 1, because: a) Keep number_of_peripherals in fpga.yaml, because this second array level is needed when the
- this is fine for exising QSYS MM bus designs fpga design uses an array of peripherals, like with N_beamsets = 2 in SDP FW.
- 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
b) Rename some keys in fpga.yaml to more clearly distinguish them from similar keys in periperal.yaml: b) Rename some keys in fpga.yaml to more clearly distinguish them from similar keys in periperal.yaml:
- periperal_name --> fpga_periperal_name - periperal_name --> fpga_periperal_name
- periperal_group --> fpga_periperal_group - periperal_group --> fpga_periperal_group
...@@ -145,14 +143,25 @@ e) field ...@@ -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? - 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 . 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 ? - 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: - radix:
. use radix u1, u8, i16, ch8, f32, d64 etc to indicate element type and width. . use radix u1, u8, i16, ch8, f32, d64 etc to indicate element type and width.
- radix_type - radix_type = radix
- radix_width - radix_width
- radix_resolution, default 0 for unit = 2**radix_resolution = 1 or not used, can be > 0 or < 0, - 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 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. 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. . fields can overlap, this allows accessing multiple fields at once or per field.
. endianess . endianess
- defined upto 8 byte data (64b), e.g.: - defined upto 8 byte data (64b), e.g.:
int64 0x0706050403020100 int64 0x0706050403020100
...@@ -163,6 +172,8 @@ e) field ...@@ -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. - 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: . add radix columns to mmap? --> yes:
. width, bit_offset, radix_width --> field bit mask [hi:lo] . 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 . radix_resolution --> field resolution
. add more radix types: . add more radix types:
width width
...@@ -174,12 +185,47 @@ e) field ...@@ -174,12 +185,47 @@ e) field
- d, double = 64b - d, double = 64b
- u, unsigned >= 1b - u, unsigned >= 1b
- s, signed >= 1b - s, signed >= 1b
- c, complex >= 2*1b --> packed signed im,re in width, so give ERROR when width % 2 != 0 - 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
. maximum width = 32 is the MM width. of the im, re, because width, or radix_width, define the total number of bits.
- allow radix_width < field_width to pack smaller elements into width. Give ERROR when (field_width % radix_width) != 0 . so pack first re at lowest part then hi at highest part, kind of
- allow radix_width > field_width to support larger elements e.g. 48b, 64b. The field is defined in the first little endian order (i.e. re first) for complex values.
32b word and extends into the next 32b word(s).
. 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: . support packing array of fields dependent on radix_width and field_width:
- keep maximum field_width = 1..32 - keep maximum field_width = 1..32
...@@ -188,10 +234,18 @@ e) field ...@@ -188,10 +234,18 @@ e) field
- the radix array size depends on: - the radix array size depends on:
. radix_array_size = number_of_fields * width / radix_width . 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: - 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 . Typically RAM and FIFO only have one field in fields, REG can have multiple fields,
. All can have field_group, although only useful for REG 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 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. - 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 ...@@ -212,7 +266,7 @@ h) mmap
- only base address and span in mmap or do also we need the fields in 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? - store spaces ' ' as single space in mmap to reduce size in FPGA?
- [i:i] or [i] ? - [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: - 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 . 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 mmap is self contained, so no need to access the yaml file
...@@ -221,8 +275,7 @@ h) mmap ...@@ -221,8 +275,7 @@ h) mmap
- only non-redundant info in mmap, not needed now: - only non-redundant info in mmap, not needed now:
. hdl_library_name, . hdl_library_name,
peripheral_name, peripheral_name,
peripheral_group, peripheral_group, because the slave port name uniquely defines the MM port, no need to know its related origin keys,
field_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. . 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 . 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 . endianness, because it values are passed on transparently, mmap does not change the endianess
...@@ -248,16 +301,75 @@ h) mmap ...@@ -248,16 +301,75 @@ h) mmap
- - field_name: bsn_init_hi - - field_name: bsn_init_hi
field_description: "Initial BSN[63:32] hi part of 64b value" field_description: "Initial BSN[63:32] hi part of 64b value"
address_offset: 0xC address_offset: 0xC
Note: When radix_width > field width + bit_offset, then the field width is don't care because then the radix
. radix resolution, because values are transparently passed on 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: - already existing columns:
. field radix type # = radix . field radix # radix type
. field bit mask # derived from width, bit_offset, radix_width . 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: 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
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment