Add support for alias registers

This required some fundamental changes. One of them is that YAML fields
are now processed in a more systematic way in which all fields are
passed via a dictionary. That way, some of the fields are not bound to
the original object anymore.
This commit is contained in:
Dennis Potter 2021-06-12 01:28:29 +02:00
parent 9deb28ce4e
commit b2c756af41
Signed by: Dennis
GPG Key ID: 186A8AD440942BAF
7 changed files with 465 additions and 340 deletions

View File

@ -23,10 +23,6 @@ class AddrMap(Component):
def __init__(self, obj: node.RootNode, config: dict): def __init__(self, obj: node.RootNode, config: dict):
super().__init__(obj, config) super().__init__(obj, config)
# Create logger object
self.create_logger(self.path, config)
self.logger.debug('Starting to process addrmap')
# Check if global resets are defined # Check if global resets are defined
glbl_settings = dict() glbl_settings = dict()
@ -44,7 +40,7 @@ class AddrMap(Component):
# We need a dictionary since it might be required to access the objects later # We need a dictionary since it might be required to access the objects later
# by name (for example, in case of aliases) # by name (for example, in case of aliases)
self.registers = dict() self.registers = dict()
self.regfiles = [] self.regfiles = dict()
# Traverse through children # Traverse through children
for child in obj.children(): for child in obj.children():
@ -54,28 +50,27 @@ class AddrMap(Component):
self.logger.info('Found hierarchical addrmap. Entering it...') self.logger.info('Found hierarchical addrmap. Entering it...')
self.logger.error('Child addrmaps are not implemented yet!') self.logger.error('Child addrmaps are not implemented yet!')
elif isinstance(child, node.RegfileNode): elif isinstance(child, node.RegfileNode):
self.regfiles.append(RegFile(child, [], [], config, glbl_settings)) self.regfiles[child.inst_name] = \
RegFile(child, [], [], config, glbl_settings)
elif isinstance(child, node.RegNode): elif isinstance(child, node.RegNode):
if child.inst.is_alias: if child.inst.is_alias:
# If the node we found is an alias, we shall not create a # If the node we found is an alias, we shall not create a
# new register. Rather, we bury up the old register and add # new register. Rather, we bury up the old register and add
# additional properties # additional properties
self.logger.error('Alias registers are not implemented yet!') self.registers[child.inst.alias_primary_inst.inst_name]\
.add_alias(child)
else: else:
self.registers[child.inst_name] = \ self.registers[child.inst_name] = \
Register(child, [], [], config, glbl_settings) Register(child, [], [], config, glbl_settings)
# Add registers to children. This must be done in a last step # Add registers to children. This must be done in a last step
# to account for all possible alias combinations # to account for all possible alias combinations
self.children = [ self.children = {**self.regfiles, **self.registers}
*self.regfiles,
*[x for x in self.registers.values()]
]
self.logger.info("Done generating all child-regfiles/registers") self.logger.info("Done generating all child-regfiles/registers")
# Add bus widget ports # Add bus widget ports
self.__add_bus_widget_ports() widget_rtl = self.__get_widget_ports_rtl()
# Start assembling addrmap module # Start assembling addrmap module
self.logger.info("Starting to assemble input & output ports") self.logger.info("Starting to assemble input & output ports")
@ -159,7 +154,7 @@ class AddrMap(Component):
self.__add_signal_instantiation() self.__add_signal_instantiation()
# Add bus widget RTL # Add bus widget RTL
self.__add_bus_widget_instantiation() self.rtl_header.append(widget_rtl)
# Append genvars # Append genvars
self.__append_genvars() self.__append_genvars()
@ -189,16 +184,15 @@ class AddrMap(Component):
'' ''
] ]
def __add_bus_widget_ports(self): def __get_widget_ports_rtl(self):
self.widget_templ_dict = yaml.load( self.widget_templ_dict = yaml.load(
pkg_resources.read_text(widgets, '{}.yaml'.format(self.config['bus'])), pkg_resources.read_text(widgets, '{}.yaml'.format(self.config['bus'])),
Loader=yaml.FullLoader) Loader=yaml.FullLoader)
self.yaml_signals_to_list(self.widget_templ_dict['module_instantiation']) return self.process_yaml(
self.widget_templ_dict['module_instantiation'],
def __add_bus_widget_instantiation(self): # TODO: Add widths
self.rtl_header.append( )
self.widget_templ_dict['module_instantiation']['rtl'])
def __append_genvars(self): def __append_genvars(self):

View File

@ -16,7 +16,7 @@ class Component():
def __init__(self, obj, config): def __init__(self, obj, config):
self.rtl_header = [] self.rtl_header = []
self.rtl_footer = [] self.rtl_footer = []
self.children = [] self.children = dict()
self.typedefs = dict() self.typedefs = dict()
self.ports = dict() self.ports = dict()
self.resets = set() self.resets = set()
@ -39,7 +39,9 @@ class Component():
# Create logger object # Create logger object
self.create_logger("{}.{}".format(self.owning_addrmap, self.path), config) self.create_logger("{}.{}".format(self.owning_addrmap, self.path), config)
self.logger.debug('Starting to process register "{}"'.format(obj.inst_name)) self.logger.debug('Starting to process {} "{}"'.format(
self.__class__.__name__,
obj.inst_name))
def create_logger(self, name: str, config: dict): def create_logger(self, name: str, config: dict):
self.logger = create_logger( self.logger = create_logger(
@ -52,7 +54,7 @@ class Component():
def get_resets(self): def get_resets(self):
self.logger.debug("Return reset list") self.logger.debug("Return reset list")
for x in self.children: for x in self.children.values():
self.resets |= x.get_resets() self.resets |= x.get_resets()
return self.resets return self.resets
@ -60,7 +62,7 @@ class Component():
def get_ports(self, port_type: str): def get_ports(self, port_type: str):
self.logger.debug("Return port list") self.logger.debug("Return port list")
for x in self.children: for x in self.children.values():
self.ports[port_type] |= x.get_ports(port_type) self.ports[port_type] |= x.get_ports(port_type)
return self.ports[port_type] return self.ports[port_type]
@ -71,14 +73,14 @@ class Component():
self.total_array_dimensions)) self.total_array_dimensions))
return max([ return max([
self.total_dimensions, self.total_dimensions,
*[x.get_max_dim_depth() for x in self.children] *[x.get_max_dim_depth() for x in self.children.values()]
]) ])
def get_signals(self, no_children = False): def get_signals(self, no_children = False):
self.logger.debug("Return signal list") self.logger.debug("Return signal list")
if not no_children: if not no_children:
for x in self.children: for x in self.children.values():
self.signals |= x.get_signals() self.signals |= x.get_signals()
return self.signals return self.signals
@ -86,7 +88,7 @@ class Component():
def get_typedefs(self): def get_typedefs(self):
self.logger.debug("Return typedef list") self.logger.debug("Return typedef list")
for x in self.children: for x in self.children.values():
self.typedefs |= x.get_typedefs() self.typedefs |= x.get_typedefs()
return self.typedefs return self.typedefs
@ -97,8 +99,16 @@ class Component():
# Loop through children and append RTL # Loop through children and append RTL
rtl_children = [] rtl_children = []
for child in self.children: for x in self.children.values():
rtl_children.append(child.get_rtl()) # In case of fields, the RTL must first be generated.
# Reason is that we only know at the very end whether
# all alias registers are processed for fields
try:
x.create_rtl()
except:
pass
rtl_children.append(x.get_rtl())
# Concatenate header, main, and footer # Concatenate header, main, and footer
rtl = [*self.rtl_header, *rtl_children, *self.rtl_footer] rtl = [*self.rtl_header, *rtl_children, *self.rtl_footer]
@ -204,31 +214,34 @@ class Component():
return ''.join(name) return ''.join(name)
def yaml_signals_to_list(self, yaml_obj): def process_yaml(self, yaml_obj, values: dict = {}):
try: try:
for x in yaml_obj['signals']: for x in yaml_obj['signals']:
self.signals[x['name'].format(path = self.path_underscored)] =\ self.signals[x['name'].format(**values)] =\
(x['signal_type'].format(field_type = self.field_type), (x['signal_type'].format(**values),
self.total_array_dimensions) self.total_array_dimensions)
except (TypeError, KeyError): except (TypeError, KeyError):
pass pass
try: try:
for x in yaml_obj['input_ports']: for x in yaml_obj['input_ports']:
self.ports['input'][x['name'].format(path = self.path_underscored)] =\ self.ports['input'][x['name'].format(**values)] =\
(x['signal_type'].format(field_type = self.field_type), (x['signal_type'].format(**values),
self.total_array_dimensions) self.total_array_dimensions)
except (TypeError, KeyError): except (TypeError, KeyError):
pass pass
try: try:
for x in yaml_obj['output_ports']: for x in yaml_obj['output_ports']:
self.ports['output'][x['name'].format(path = self.path_underscored)] =\ self.ports['output'][x['name'].format(**values)] =\
(x['signal_type'].format(field_type = self.field_type), (x['signal_type'].format(**values),
self.total_array_dimensions) self.total_array_dimensions)
except (TypeError, KeyError): except (TypeError, KeyError):
pass pass
# Return RTL with values
return yaml_obj['rtl'].format(**values)
@staticmethod @staticmethod
def process_reset_signal(reset_signal): def process_reset_signal(reset_signal):
rst = dict() rst = dict()
@ -256,9 +269,16 @@ class Component():
return rst return rst
def create_underscored_path(self): def create_underscored_path(self):
self.owning_addrmap = self.obj.owning_addrmap.inst_name self.owning_addrmap, self.full_path, self.path, self.path_underscored =\
self.full_path = Component.split_dimensions(self.obj.get_path())[0] Component.create_underscored_path_static(self.obj)
self.path = self.full_path\
.replace('{}.'.format(self.owning_addrmap), '')
self.path_underscored = self.path.replace('.', '__') @staticmethod
def create_underscored_path_static(obj):
owning_addrmap = obj.owning_addrmap.inst_name
full_path = Component.split_dimensions(obj.get_path())[0]
path = full_path\
.replace('{}.'.format(owning_addrmap), '')
path_underscored = path.replace('.', '__')
return (owning_addrmap, full_path, path, path_underscored)

View File

@ -39,12 +39,266 @@ class Field(Component):
# Print a summary # Print a summary
self.rtl_header.append(self.summary()) self.rtl_header.append(self.summary())
# Generate RTL # HW Access can be handled in __init__ function but SW access
# must be handled in a seperate method that can be called
# seperately in case of alias registers
self.__add_always_ff() self.__add_always_ff()
self.__add_access_rtl() self.__add_hw_access()
self.__add_combo() self.__add_combo()
self.__add_ports()
self.__prepend_signal_declarations() self.add_sw_access(obj)
def add_sw_access(self, obj, alias = False):
access_rtl = dict()
if alias:
owning_addrmap, full_path, path, path_underscored =\
Field.create_underscored_path_static(obj)
else:
owning_addrmap, full_path, path, path_underscored =\
self.owning_addrmap, self.full_path, self.path, self.path_underscored
path_wo_field = '__'.join(path.split('.', -1)[0:-1])
# Define software access (if applicable)
access_rtl['sw_write'] = ([], False)
if obj.get_property('sw') in (AccessType.rw, AccessType.w):
swwe = obj.get_property('swwe')
swwel = obj.get_property('swwel')
if isinstance(swwe, (FieldNode, SignalNode)):
access_rtl['sw_write'][0].append(
self.process_yaml(
Field.templ_dict['sw_access_field_swwe'],
{'path_wo_field': path_wo_field,
'genvars': self.genvars_str,
'swwe': self.get_signal_name(swwe),
'field_type': self.field_type}
)
)
elif isinstance(swwel, (FieldNode, SignalNode)):
access_rtl['sw_write'][0].append(
self.process_yaml(
Field.templ_dict['sw_access_field_swwel'],
{'path_wo_field': path_wo_field,
'genvars': self.genvars_str,
'swwel': self.get_signal_name(swwel),
'field_type': self.field_type}
)
)
else:
access_rtl['sw_write'][0].append(
self.process_yaml(
Field.templ_dict['sw_access_field'],
{'path_wo_field': path_wo_field,
'genvars': self.genvars_str,
'field_type': self.field_type}
)
)
# Check if an onwrite property is set
onwrite = obj.get_property('onwrite')
if onwrite:
if onwrite == OnWriteType.wuser:
self.logger.warning("The OnReadType.wuser is not yet supported!")
elif onwrite in (OnWriteType.wclr, OnWriteType.wset):
access_rtl['sw_write'][0].append(
self.process_yaml(
Field.templ_dict[str(onwrite)],
{'path': path_underscored,
'genvars': self.genvars_str,
'width': obj.width,
'path_wo_field': path_wo_field,
'field_type': self.field_type}
)
)
else:
# If field spans multiple bytes, every byte shall have a seperate enable!
for j, i in enumerate(range(self.lsbyte, self.msbyte+1)):
access_rtl['sw_write'][0].append(
self.process_yaml(
Field.templ_dict[str(onwrite)],
{'path': path_underscored,
'genvars': self.genvars_str,
'i': i,
'width': obj.width,
'msb_bus': str(8*(i+1)-1 if i != self.msbyte else obj.msb),
'bus_w': str(8 if i != self.msbyte else obj.width-(8*j)),
'msb_field': str(8*(j+1)-1 if i != self.msbyte else obj.width-1),
'field_w': str(8 if i != self.msbyte else obj.width-(8*j)),
'field_type': self.field_type}
)
)
else:
# Normal write
# If field spans multiple bytes, every byte shall have a seperate enable!
for j, i in enumerate(range(self.lsbyte, self.msbyte+1)):
access_rtl['sw_write'][0].append(
self.process_yaml(
Field.templ_dict['sw_access_byte'],
{'path': self.path_underscored,
'genvars': self.genvars_str,
'i': i,
'msb_bus': str(8*(i+1)-1 if i != self.msbyte else obj.msb),
'bus_w': str(8 if i != self.msbyte else obj.width-(8*j)),
'msb_field': str(8*(j+1)-1 if i != self.msbyte else obj.width-1),
'field_w': str(8 if i != self.msbyte else obj.width-(8*j)),
'field_type': self.field_type}
)
)
access_rtl['sw_write'][0].append("end")
onread = obj.get_property('onread')
access_rtl['sw_read'] = ([], False)
if obj.get_property('sw') in (AccessType.rw, AccessType.r) and onread:
if onread == OnReadType.ruser:
self.logger.warning("The OnReadType.ruser is not yet supported!")
else:
access_rtl['sw_read'][0].append(
self.process_yaml(
Field.templ_dict[str(onread)],
{'width': obj.width,
'path': path_underscored,
'genvars': self.genvars_str,
'path_wo_field': path_wo_field}
)
)
# Add singlepulse property
# Property cannot be overwritten by alias
if obj.get_property('singlepulse'):
self.access_rtl['singlepulse'] = ([
self.process_yaml(
Field.templ_dict['singlepulse'],
{'path': self.path_underscored,
'genvars': self.genvars_str}
)
],
True)
else:
self.access_rtl['singlepulse'] = ([], False)
# Add to global dictionary
try:
# Alias, so add 'else'
self.access_rtl['sw_read'] = \
[*self.access_rtl['sw_read'], access_rtl['sw_read']]
self.access_rtl['sw_write'] = \
[*self.access_rtl['sw_write'], access_rtl['sw_write']]
except KeyError:
self.access_rtl['sw_read'] = [access_rtl['sw_read']]
self.access_rtl['sw_write'] = [access_rtl['sw_write']]
def __add_hw_access(self):
# Define hardware access (if applicable)
if self.obj.get_property('hw') in (AccessType.rw, AccessType.w):
write_condition = 'hw_access_we_wel' if self.we_or_wel else 'hw_access_no_we_wel'
# if-line of hw-access
self.access_rtl['hw_write'] = ([
self.process_yaml(
Field.templ_dict[write_condition],
{'negl': '!' if self.obj.get_property('wel') else '',
'path': self.path_underscored,
'genvars': self.genvars_str,
'field_type': self.field_type}
)
],
write_condition == 'hw_access_no_we_wel') # Abort if no condition is set
# Actual assignment of register
self.access_rtl['hw_write'][0].append(
self.process_yaml(
Field.templ_dict['hw_access_field'],
{'path': self.path_underscored,
'genvars': self.genvars_str,
'field_type': self.field_type}
)
)
else:
self.access_rtl['hw_write'] = ([], False)
# Hookup flop to output port in case register is readable by hardware
if self.obj.get_property('hw') in (AccessType.rw, AccessType.r):
# Connect flops to output port
self.rtl_footer.append(
self.process_yaml(
Field.templ_dict['out_port_assign'],
{'genvars': self.genvars_str,
'path': self.path_underscored,
'field_type': self.field_type}
)
)
def create_rtl(self):
# Not all access types are required and the order might differ
# depending on what types are defined and what precedence is
# set. Therefore, first add all RTL into a dictionary and
# later place it in the right order.
#
# Check if hardware has precedence (default `precedence = sw`)
if self.obj.get_property('precedence') == PrecedenceType.sw:
order_list = [
'sw_write',
'sw_read',
'hw_write',
'singlepulse'
]
else:
order_list = [
'hw_write',
'sw_write',
'sw_read',
'singlepulse'
]
# Add appropriate else
order_list_rtl = []
abort_set = False
for i in order_list:
# Still a loop and not a list comprehension since this might
# get longer in the future and thus become unreadable
# First check if we need to break or continue the loop
if abort_set:
break
# Check if there is a list that shall be unlooped
if isinstance(self.access_rtl[i], tuple):
access_rtl = [self.access_rtl[i]]
else:
access_rtl = self.access_rtl[i]
for unpacked_access_rtl in access_rtl:
if len(unpacked_access_rtl[0]) == 0:
continue
order_list_rtl = [*order_list_rtl, *unpacked_access_rtl[0]]
order_list_rtl.append("else")
# If the access_rtl entry has an abortion entry, do not print
# any further branches of the conditional block
abort_set = unpacked_access_rtl[1]
# Remove last else
order_list_rtl.pop()
# Chain access RTL to the rest of the RTL
self.rtl_header = [*self.rtl_header, *order_list_rtl]
self.rtl_header.append(
self.process_yaml(
Field.templ_dict['end_field_ff'],
{'path': self.path_underscored}
)
)
def __add_combo(self): def __add_combo(self):
operations = [] operations = []
@ -56,20 +310,24 @@ class Field(Component):
operations.append(['^', 'assign_xored_operation']) operations.append(['^', 'assign_xored_operation'])
if len(operations) > 0: if len(operations) > 0:
self.rtl_header.append( self.rtl_footer.append(
Field.templ_dict['combo_operation_comment']['rtl'].format( self.process_yaml(
path = self.path_underscored)) Field.templ_dict['combo_operation_comment'],
{'path': self.path_underscored}
)
)
self.rtl_header = [ self.rtl_footer = [
*self.rtl_header, *self.rtl_footer,
*[Field.templ_dict[i[1]]['rtl'].format( *[self.process_yaml(
path = self.path_underscored, Field.templ_dict[i[1]],
genvars = self.genvars_str, {'path': self.path_underscored,
op_verilog = i[0]) for i in operations] 'genvars': self.genvars_str,
'op_verilog': i[0],
'field_type': self.field_type}
) for i in operations]
] ]
[self.yaml_signals_to_list(Field.templ_dict[i[1]]) for i in operations]
def __process_fieldtype(self): def __process_fieldtype(self):
try: try:
@ -174,11 +432,10 @@ class Field(Component):
'\'x' if not obj.get_property("reset") else\ '\'x' if not obj.get_property("reset") else\
obj.get_property('reset') obj.get_property('reset')
# Define hardware access # Define dict that holds all RTL
self.hw_access = obj.get_property('hw') self.access_rtl = dict()
self.sw_access = obj.get_property('sw') self.access_rtl['else'] = (["else"], False)
self.precedence = obj.get_property('precedence') self.access_rtl[''] = ([''], False)
def summary(self): def summary(self):
# Additional flags that are set # Additional flags that are set
@ -189,14 +446,16 @@ class Field(Component):
misc_flags.discard('hw') misc_flags.discard('hw')
misc_flags.discard('reset') misc_flags.discard('reset')
precedence = self.obj.get_property('precedence')
# Add comment with summary on field's properties # Add comment with summary on field's properties
return \ return \
Field.templ_dict['field_comment']['rtl'].format( Field.templ_dict['field_comment']['rtl'].format(
name = self.name, name = self.name,
hw_access = str(self.hw_access)[11:], hw_access = str(self.obj.get_property('hw'))[11:],
sw_access = str(self.sw_access)[11:], sw_access = str(self.obj.get_property('sw'))[11:],
hw_precedence = '(precedence)' if self.precedence == PrecedenceType.hw else '', hw_precedence = '(precedence)' if precedence == PrecedenceType.hw else '',
sw_precedence = '(precedence)' if self.precedence == PrecedenceType.sw else '', sw_precedence = '(precedence)' if precedence == PrecedenceType.sw else '',
rst_active = self.rst['active'], rst_active = self.rst['active'],
rst_type = self.rst['type'], rst_type = self.rst['type'],
misc_flags = misc_flags if misc_flags else '-', misc_flags = misc_flags if misc_flags else '-',
@ -209,228 +468,38 @@ class Field(Component):
sense_list = 'sense_list_rst' if self.rst['async'] else 'sense_list_no_rst' sense_list = 'sense_list_rst' if self.rst['async'] else 'sense_list_no_rst'
self.rtl_header.append( self.rtl_header.append(
Field.templ_dict[sense_list]['rtl'].format( self.process_yaml(
rst_edge = self.rst['edge'], Field.templ_dict[sense_list],
rst_name = self.rst['name'])) {'rst_edge': self.rst['edge'],
'rst_name': self.rst['name']}
)
)
# Add actual reset line # Add actual reset line
if self.rst['name']: if self.rst['name']:
self.rtl_header.append( self.rtl_header.append(
Field.templ_dict['rst_field_assign']['rtl'].format( self.process_yaml(
path = self.path_underscored, Field.templ_dict['rst_field_assign'],
rst_name = self.rst['name'], {'path': self.path_underscored,
rst_negl = "!" if self.rst['active'] == "active_low" else "", 'rst_name': self.rst['name'],
rst_value = self.rst['value'], 'rst_negl': "!" if self.rst['active'] == "active_low" else "",
genvars = self.genvars_str)) 'rst_value': self.rst['value'],
'genvars': self.genvars_str,
self.yaml_signals_to_list(Field.templ_dict['rst_field_assign']) 'field_type': self.field_type}
)
)
self.rtl_header.append("begin") self.rtl_header.append("begin")
# Add name of actual field to Signal field # Add name of actual field to Signal field
# TODO # TODO
def __prepend_signal_declarations(self):
pass
def __add_access_rtl(self):
# Not all access types are required and the order might differ
# depending on what types are defined and what precedence is
# set. Therefore, first add all RTL into a dictionary and
# later place it in the right order.
#
# The following RTL blocks are defined:
# - hw_write --> write access for the hardware interface
# - sw_write --> write access for the software interface
#
access_rtl = dict()
# Define hardware access (if applicable)
if self.hw_access in (AccessType.rw, AccessType.w):
write_condition = 'hw_access_we_wel' if self.we_or_wel else 'hw_access_no_we_wel'
# if-line of hw-access
access_rtl['hw_write'] = ([
Field.templ_dict[write_condition]['rtl'].format(
negl = '!' if self.obj.get_property('wel') else '',
path = self.path_underscored,
genvars = self.genvars_str)
],
write_condition == 'hw_access_no_we_wel') # Abort if no condition is set
# Actual assignment of register
access_rtl['hw_write'][0].append(
Field.templ_dict['hw_access_field']['rtl'].format(
path = self.path_underscored,
genvars = self.genvars_str))
# Get ports/signals from list
self.yaml_signals_to_list(Field.templ_dict[write_condition])
self.yaml_signals_to_list(Field.templ_dict['hw_access_field'])
else:
access_rtl['hw_write'] = ([], False)
# Define software access (if applicable)
access_rtl['sw_write'] = ([], False)
if self.sw_access in (AccessType.rw, AccessType.w):
swwe = self.obj.get_property('swwe')
swwel = self.obj.get_property('swwel')
if isinstance(swwe, (FieldNode, SignalNode)):
access_rtl['sw_write'][0].append(
Field.templ_dict['sw_access_field_swwe']['rtl'].format(
path_wo_field = self.path_wo_field,
genvars = self.genvars_str,
swwe = self.get_signal_name(swwe)))
elif isinstance(swwel, (FieldNode, SignalNode)):
access_rtl['sw_write'][0].append(
Field.templ_dict['sw_access_field_swwel']['rtl'].format(
path_wo_field = self.path_wo_field,
genvars = self.genvars_str,
swwel = self.get_signal_name(swwel)))
else:
access_rtl['sw_write'][0].append(
Field.templ_dict['sw_access_field']['rtl'].format(
path_wo_field = self.path_wo_field,
genvars = self.genvars_str))
# Check if an onwrite property is set
onwrite = self.obj.get_property('onwrite')
if onwrite:
if onwrite == OnWriteType.wuser:
self.logger.warning("The OnReadType.wuser is not yet supported!")
elif onwrite in (OnWriteType.wclr, OnWriteType.wset):
access_rtl['sw_write'][0].append(
Field.templ_dict[str(onwrite)]['rtl'].format(
path = self.path_underscored,
genvars = self.genvars_str,
width = self.obj.width,
path_wo_field = self.path_wo_field
)
)
else:
# If field spans multiple bytes, every byte shall have a seperate enable!
for j, i in enumerate(range(self.lsbyte, self.msbyte+1)):
access_rtl['sw_write'][0].append(
Field.templ_dict[str(onwrite)]['rtl'].format(
path = self.path_underscored,
genvars = self.genvars_str,
i = i,
width = self.obj.width,
msb_bus = str(8*(i+1)-1 if i != self.msbyte else self.obj.msb),
bus_w = str(8 if i != self.msbyte else self.obj.width-(8*j)),
msb_field = str(8*(j+1)-1 if i != self.msbyte else self.obj.width-1),
field_w = str(8 if i != self.msbyte else self.obj.width-(8*j))))
else:
# Normal write
# If field spans multiple bytes, every byte shall have a seperate enable!
for j, i in enumerate(range(self.lsbyte, self.msbyte+1)):
access_rtl['sw_write'][0].append(
Field.templ_dict['sw_access_byte']['rtl'].format(
path = self.path_underscored,
genvars = self.genvars_str,
i = i,
msb_bus = str(8*(i+1)-1 if i != self.msbyte else self.obj.msb),
bus_w = str(8 if i != self.msbyte else self.obj.width-(8*j)),
msb_field = str(8*(j+1)-1 if i != self.msbyte else self.obj.width-1),
field_w = str(8 if i != self.msbyte else self.obj.width-(8*j))))
access_rtl['sw_write'][0].append("end")
onread = self.obj.get_property('onread')
access_rtl['sw_read'] = ([], False)
if self.sw_access in (AccessType.rw, AccessType.r) and onread:
if onread == OnReadType.ruser:
self.logger.warning("The OnReadType.ruser is not yet supported!")
else:
access_rtl['sw_read'][0].append(
Field.templ_dict[str(onread)]['rtl'].format(
width = self.obj.width,
path = self.path_underscored,
genvars = self.genvars_str,
path_wo_field = self.path_wo_field
)
)
# Add singlepulse property
if self.obj.get_property('singlepulse'):
access_rtl['singlepulse'] = ([
Field.templ_dict['singlepulse']['rtl'].format(
path = self.path_underscored,
genvars = self.genvars_str)
],
True)
else:
access_rtl['singlepulse'] = ([], False)
# Define else
access_rtl['else'] = (["else"], False)
# Add empty string
access_rtl[''] = ([''], False)
# Check if hardware has precedence (default `precedence = sw`)
if self.precedence == PrecedenceType.sw:
order_list = [
'sw_write',
'sw_read',
'hw_write',
'singlepulse'
]
else:
order_list = [
'hw_write',
'sw_write',
'sw_read',
'singlepulse'
]
# Add appropriate else
order_list_rtl = []
abort_set = False
for i in order_list:
# Still a loop and not a list comprehension since this might
# get longer in the future and thus become unreadable
if len(access_rtl[i][0]) and not abort_set:
order_list_rtl = [*order_list_rtl, *access_rtl[i][0]]
order_list_rtl.append("else")
# If he access_rtl entry has an abortion entry, do not print
# any further branches of the conditional block
abort_set = access_rtl[i][1]
# Remove last else
order_list_rtl.pop()
# Chain access RTL to the rest of the RTL
self.rtl_header = [*self.rtl_header, *order_list_rtl]
self.rtl_header.append(
Field.templ_dict['end_field_ff']['rtl'].format(
path = self.path_underscored))
def __add_ports(self):
if self.hw_access in (AccessType.rw, AccessType.r):
# Connect flops to output port
self.rtl_header.append(
Field.templ_dict['out_port_assign']['rtl'].format(
genvars = self.genvars_str,
path = self.path_underscored))
self.yaml_signals_to_list(Field.templ_dict['out_port_assign'])
def sanity_checks(self): def sanity_checks(self):
# If hw=rw/sw=[r]w and hw has no we/wel, sw will never be able to write # If hw=rw/sw=[r]w and hw has no we/wel, sw will never be able to write
if not self.we_or_wel and\ if not self.we_or_wel and\
self.precedence == PrecedenceType.hw and \ self.obj.get_property('precedence') == PrecedenceType.hw and \
self.hw_access in (AccessType.rw, AccessType.w) and \ self.obj.get_property('hw') in (AccessType.rw, AccessType.w) and \
self.sw_access in (AccessType.rw, AccessType.w): self.obj.get_property('sw') in (AccessType.rw, AccessType.w):
self.logger.warning("Fields with hw=rw/sw=[r]w, we/wel not set and "\ self.logger.warning("Fields with hw=rw/sw=[r]w, we/wel not set and "\
"precedence for hardware will render software's "\ "precedence for hardware will render software's "\

View File

@ -34,18 +34,23 @@ class RegFile(Component):
# Create comment and provide user information about register he/she # Create comment and provide user information about register he/she
# is looking at. # is looking at.
self.rtl_header = [ self.rtl_header = [
RegFile.templ_dict['regfile_comment']['rtl'].format( self.process_yaml(
name = obj.inst_name, RegFile.templ_dict['regfile_comment'],
dimensions = self.dimensions, {'name': obj.inst_name,
depth = self.depth), 'dimensions': self.dimensions,
*self.rtl_header 'depth': self.depth}
),
*self.rtl_header
] ]
# Create generate block for register and add comment # Create generate block for register and add comment
for i in range(self.dimensions-1, -1, -1): for i in range(self.dimensions-1, -1, -1):
self.rtl_footer.append( self.rtl_footer.append(
RegFile.templ_dict['generate_for_end']['rtl'].format( self.process_yaml(
dimension = chr(97+i))) RegFile.templ_dict['generate_for_end'],
{'dimension': chr(97+i)}
)
)
if self.dimensions and not glbl_settings['generate_active']: if self.dimensions and not glbl_settings['generate_active']:
self.rtl_header.append("generate") self.rtl_header.append("generate")
@ -56,15 +61,18 @@ class RegFile(Component):
for i in range(self.dimensions): for i in range(self.dimensions):
self.rtl_header.append( self.rtl_header.append(
RegFile.templ_dict['generate_for_start']['rtl'].format( self.process_yaml(
iterator = chr(97+i+self.parents_depths), RegFile.templ_dict['generate_for_start'],
limit = self.array_dimensions[i])) {'iterator': chr(97+i+self.parents_depths),
'limit': self.array_dimensions[i]}
)
)
# Empty dictionary of register objects # Empty dictionary of register objects
# We need a dictionary since it might be required to access the objects later # We need a dictionary since it might be required to access the objects later
# by name (for example, in case of aliases) # by name (for example, in case of aliases)
self.registers = dict() self.registers = dict()
self.regfiles = [] self.regfiles = dict()
# Set object to 0 for easy addressing # Set object to 0 for easy addressing
self.obj.current_idx = [0] self.obj.current_idx = [0]
@ -79,19 +87,20 @@ class RegFile(Component):
elif isinstance(child, node.RegfileNode): elif isinstance(child, node.RegfileNode):
self.obj.current_idx = [0] self.obj.current_idx = [0]
self.regfiles.append( self.regfiles[child.inst_name] = \
RegFile( RegFile(
child, child,
self.total_array_dimensions, self.total_array_dimensions,
self.total_stride, self.total_stride,
config, config,
glbl_settings)) glbl_settings)
elif isinstance(child, node.RegNode): elif isinstance(child, node.RegNode):
if child.inst.is_alias: if child.inst.is_alias:
# If the node we found is an alias, we shall not create a # If the node we found is an alias, we shall not create a
# new register. Rather, we bury up the old register and add # new register. Rather, we bury up the old register and add
# additional properties # additional properties
self.logger.error('Alias registers are not implemented yet!') self.registers[child.inst.alias_primary_inst.inst_name]\
.add_alias(child)
else: else:
self.obj.current_idx = [0] self.obj.current_idx = [0]
self.registers[child.inst_name] = \ self.registers[child.inst_name] = \
@ -104,10 +113,7 @@ class RegFile(Component):
# Add registers to children. This must be done in a last step # Add registers to children. This must be done in a last step
# to account for all possible alias combinations # to account for all possible alias combinations
self.children = [ self.children = {**self.regfiles, **self.registers}
*self.regfiles,
*[x for x in self.registers.values()]
]
self.logger.info("Done generating all child-regfiles/registers") self.logger.info("Done generating all child-regfiles/registers")

View File

@ -1,6 +1,7 @@
import importlib.resources as pkg_resources import importlib.resources as pkg_resources
import yaml
import math import math
import sys
import yaml
from systemrdl import node from systemrdl import node
@ -25,43 +26,59 @@ class Register(Component):
super().__init__(obj, config) super().__init__(obj, config)
# Save and/or process important variables # Save and/or process important variables
self.__process_variables(obj, parents_dimensions, parents_stride) self.__process_variables(obj, parents_dimensions, parents_stride, glbl_settings)
# Create RTL for fields # Create RTL for fields
# Fields should be in order in RTL,therefore, use list # Fields should be in order in RTL, therefore, use list
for field in obj.fields(): for field in obj.fields():
field_obj = Field(field, self.total_array_dimensions, config, glbl_settings) # Use range to save field in an array. Reason is, names are allowed to
# change when using an alias
field_range = ':'.join(map(str, [field.msb, field.lsb]))
self.children[field_range] = Field(field,
self.total_array_dimensions,
config,
glbl_settings)
if not config['disable_sanity']: if not config['disable_sanity']:
field_obj.sanity_checks() self.children[field_range].sanity_checks()
self.children.append(field_obj)
def create_rtl(self):
# Create generate block for register and add comment # Create generate block for register and add comment
if self.dimensions and not glbl_settings['generate_active']: if self.dimensions and not self.generate_active:
self.rtl_header.append("generate") self.rtl_header.append("generate")
glbl_settings['generate_active'] = True
self.generate_initiated = True
else:
self.generate_initiated = False
# Add N layers of for-loop starts
for i in range(self.dimensions): for i in range(self.dimensions):
self.rtl_header.append( self.rtl_header.append(
Register.templ_dict['generate_for_start'].format( Register.templ_dict['generate_for_start'].format(
iterator = chr(97+i+self.parents_depths), iterator = chr(97+i+self.parents_depths),
limit = self.array_dimensions[i])) limit = self.array_dimensions[i]))
# Add decoders for all registers & aliases
self.__add_address_decoder()
# End loops # Fields will be added by get_rtl()
# Add N layers of for-loop end
for i in range(self.dimensions-1, -1, -1): for i in range(self.dimensions-1, -1, -1):
self.rtl_footer.append( self.rtl_footer.append(
Register.templ_dict['generate_for_end'].format( Register.templ_dict['generate_for_end'].format(
dimension = chr(97+i))) dimension = chr(97+i)))
if self.generate_initiated: if self.dimensions and not self.generate_active:
glbl_settings['generate_active'] = False self.rtl_footer.append("endgenerate\n")
self.rtl_footer.append("endgenerate")
# Create comment and provide user information about register he/she is looking at
self.rtl_header = [
Register.templ_dict['reg_comment'].format(
name = obj.inst_name,
dimensions = self.dimensions,
depth = self.depth),
*self.rtl_header
]
def __add_address_decoder(self):
# Assign variables from bus # Assign variables from bus
self.obj.current_idx = [0] self.obj.current_idx = [0]
@ -70,15 +87,18 @@ class Register(Component):
else: else:
rw_wire_assign_field = 'rw_wire_assign_1_dim' rw_wire_assign_field = 'rw_wire_assign_1_dim'
self.rtl_header.append( [self.rtl_header.append(
Register.templ_dict[rw_wire_assign_field]['rtl'].format( self.process_yaml(
path = self.path_underscored, Register.templ_dict[rw_wire_assign_field],
addr = self.obj.absolute_address, {'path': x[0],
genvars = self.genvars_str, 'addr': x[1],
genvars_sum =self.genvars_sum_str, 'alias': '(alias)' if i > 0 else '',
depth = self.depth)) 'genvars': self.genvars_str,
'genvars_sum': self.genvars_sum_str,
self.yaml_signals_to_list(Register.templ_dict[rw_wire_assign_field]) 'depth': self.depth,
'field_type': self.field_type}
)
) for i, x in enumerate(self.alias_names)]
# Add wire/register instantiations # Add wire/register instantiations
dict_list = [(key, value) for (key, value) in self.get_signals().items()] dict_list = [(key, value) for (key, value) in self.get_signals().items()]
@ -103,31 +123,48 @@ class Register(Component):
*self.rtl_header, *self.rtl_header,
] ]
# Create comment and provide user information about register he/she def add_alias(self, obj: node.RegNode):
# is looking at. for field in obj.fields():
self.rtl_header = [ # Use range to save field in an array. Reason is, names are allowed to
Register.templ_dict['reg_comment'].format( # change when using an alias
name = obj.inst_name, field_range = ':'.join(map(str, [field.msb, field.lsb]))
dimensions = self.dimensions,
depth = self.depth),
*self.rtl_header
]
try:
self.children[field_range].add_sw_access(field, alias=True)
except KeyError:
self.logger.fatal("Range of field '{}' in alias register '{}' does "
"not correspond to range of field in original "
"register '{}'. This is illegal according to 10.5.1 b)"
"of the SystemRDL 2.0 LRM.". format(
field.inst_name,
obj.inst_name,
self.name))
sys.exit(1)
# Add name to list
self.obj.current_idx = [0]
self.alias_names.append((self.create_underscored_path_static(obj)[3], obj.absolute_address))
def __process_variables( def __process_variables(
self, self,
obj: node.RegNode, obj: node.RegNode,
parents_dimensions: list, parents_dimensions: list,
parents_stride: list): parents_stride: list,
glbl_settings: dict):
# Save object # Save object
self.obj = obj self.obj = obj
# Save name # Save name
self.obj.current_idx = [0]
self.name = obj.inst_name self.name = obj.inst_name
self.alias_names = [(self.create_underscored_path_static(obj)[3], obj.absolute_address)]
# Create full name # Create full name
self.create_underscored_path() self.create_underscored_path()
# Gnerate already started?
self.generate_active = glbl_settings['generate_active']
# Determine dimensions of register # Determine dimensions of register
if obj.is_array: if obj.is_array:
self.sel_arr = 'array' self.sel_arr = 'array'

View File

@ -33,7 +33,9 @@ sw_access_byte:
begin begin
{path}_q{genvars}[{msb_field}-:{field_w}] <= sw_wr_bus[{msb_bus}-:{bus_w}]; {path}_q{genvars}[{msb_field}-:{field_w}] <= sw_wr_bus[{msb_bus}-:{bus_w}];
end end
signals:
- name: '{path}_q'
signal_type: '{field_type}'
hw_access_we_wel: hw_access_we_wel:
rtl: |- rtl: |-
if ({negl}{path}_hw_wr{genvars}) if ({negl}{path}_hw_wr{genvars})

View File

@ -1,34 +1,31 @@
--- ---
rw_wire_assign_1_dim: rw_wire_assign_1_dim:
rtl: | rtl: |
// Assign register-activation signals // Register-activation for '{path}' {alias}
assign {path}_reg_active{genvars} = addr == {addr}; assign {path}_accss = addr == {addr};
assign {path}_sw_wr = {path}_accss && r_vld;
assign {path}_sw_wr = {path}_reg_active && r_vld; assign {path}_sw_rd = {path}_accss && w_vld;
assign {path}_sw_rd = {path}_reg_active && w_vld;
signals: signals:
- name: '{path}_sw_wr' - name: '{path}_sw_wr'
signal_type: 'logic' signal_type: 'logic'
- name: '{path}_sw_rd' - name: '{path}_sw_rd'
signal_type: 'logic' signal_type: 'logic'
- name: '{path}_reg_active' - name: '{path}_accss'
signal_type: 'logic' signal_type: 'logic'
input_ports: input_ports:
output_ports: output_ports:
rw_wire_assign_multi_dim: rw_wire_assign_multi_dim:
rtl: | rtl: |
// Register-activation for '{path}' {alias}
// Assign register-activation signals assign {path}_accss{genvars} = addr == {addr}+({genvars_sum});
assign {path}_reg_active{genvars} = addr == {addr}+({genvars_sum}); assign {path}_sw_wr{genvars} = {path}_accss{genvars} && r_vld;
assign {path}_sw_rd{genvars} = {path}_accss{genvars} && w_vld;
assign {path}_sw_wr{genvars} = {path}_reg_active{genvars} && r_vld;
assign {path}_sw_rd{genvars} = {path}_reg_active{genvars} && w_vld;
signals: signals:
- name: '{path}_sw_wr' - name: '{path}_sw_wr'
signal_type: 'logic' signal_type: 'logic'
- name: '{path}_sw_rd' - name: '{path}_sw_rd'
signal_type: 'logic' signal_type: 'logic'
- name: '{path}_reg_active' - name: '{path}_accss'
signal_type: 'logic' signal_type: 'logic'
input_ports: input_ports:
output_ports: output_ports: