diff --git a/srdl2sv/components/addrmap.py b/srdl2sv/components/addrmap.py index 84c7640..97f7afa 100644 --- a/srdl2sv/components/addrmap.py +++ b/srdl2sv/components/addrmap.py @@ -23,10 +23,6 @@ class AddrMap(Component): def __init__(self, obj: node.RootNode, config: dict): 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 glbl_settings = dict() @@ -44,7 +40,7 @@ class AddrMap(Component): # We need a dictionary since it might be required to access the objects later # by name (for example, in case of aliases) self.registers = dict() - self.regfiles = [] + self.regfiles = dict() # Traverse through children for child in obj.children(): @@ -54,28 +50,27 @@ class AddrMap(Component): self.logger.info('Found hierarchical addrmap. Entering it...') self.logger.error('Child addrmaps are not implemented yet!') 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): if child.inst.is_alias: # If the node we found is an alias, we shall not create a # new register. Rather, we bury up the old register and add # additional properties - self.logger.error('Alias registers are not implemented yet!') + self.registers[child.inst.alias_primary_inst.inst_name]\ + .add_alias(child) else: self.registers[child.inst_name] = \ Register(child, [], [], config, glbl_settings) # Add registers to children. This must be done in a last step # to account for all possible alias combinations - self.children = [ - *self.regfiles, - *[x for x in self.registers.values()] - ] + self.children = {**self.regfiles, **self.registers} self.logger.info("Done generating all child-regfiles/registers") # Add bus widget ports - self.__add_bus_widget_ports() + widget_rtl = self.__get_widget_ports_rtl() # Start assembling addrmap module self.logger.info("Starting to assemble input & output ports") @@ -159,7 +154,7 @@ class AddrMap(Component): self.__add_signal_instantiation() # Add bus widget RTL - self.__add_bus_widget_instantiation() + self.rtl_header.append(widget_rtl) # 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( pkg_resources.read_text(widgets, '{}.yaml'.format(self.config['bus'])), Loader=yaml.FullLoader) - self.yaml_signals_to_list(self.widget_templ_dict['module_instantiation']) - - def __add_bus_widget_instantiation(self): - self.rtl_header.append( - self.widget_templ_dict['module_instantiation']['rtl']) + return self.process_yaml( + self.widget_templ_dict['module_instantiation'], + # TODO: Add widths + ) def __append_genvars(self): diff --git a/srdl2sv/components/component.py b/srdl2sv/components/component.py index e38be92..6a8bfd2 100644 --- a/srdl2sv/components/component.py +++ b/srdl2sv/components/component.py @@ -16,7 +16,7 @@ class Component(): def __init__(self, obj, config): self.rtl_header = [] self.rtl_footer = [] - self.children = [] + self.children = dict() self.typedefs = dict() self.ports = dict() self.resets = set() @@ -39,7 +39,9 @@ class Component(): # Create logger object 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): self.logger = create_logger( @@ -52,7 +54,7 @@ class Component(): def get_resets(self): self.logger.debug("Return reset list") - for x in self.children: + for x in self.children.values(): self.resets |= x.get_resets() return self.resets @@ -60,7 +62,7 @@ class Component(): def get_ports(self, port_type: str): 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) return self.ports[port_type] @@ -71,14 +73,14 @@ class Component(): self.total_array_dimensions)) return max([ 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): self.logger.debug("Return signal list") if not no_children: - for x in self.children: + for x in self.children.values(): self.signals |= x.get_signals() return self.signals @@ -86,7 +88,7 @@ class Component(): def get_typedefs(self): self.logger.debug("Return typedef list") - for x in self.children: + for x in self.children.values(): self.typedefs |= x.get_typedefs() return self.typedefs @@ -97,8 +99,16 @@ class Component(): # Loop through children and append RTL rtl_children = [] - for child in self.children: - rtl_children.append(child.get_rtl()) + for x in self.children.values(): + # 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 rtl = [*self.rtl_header, *rtl_children, *self.rtl_footer] @@ -204,31 +214,34 @@ class Component(): return ''.join(name) - def yaml_signals_to_list(self, yaml_obj): + def process_yaml(self, yaml_obj, values: dict = {}): try: for x in yaml_obj['signals']: - self.signals[x['name'].format(path = self.path_underscored)] =\ - (x['signal_type'].format(field_type = self.field_type), + self.signals[x['name'].format(**values)] =\ + (x['signal_type'].format(**values), self.total_array_dimensions) except (TypeError, KeyError): pass try: for x in yaml_obj['input_ports']: - self.ports['input'][x['name'].format(path = self.path_underscored)] =\ - (x['signal_type'].format(field_type = self.field_type), + self.ports['input'][x['name'].format(**values)] =\ + (x['signal_type'].format(**values), self.total_array_dimensions) except (TypeError, KeyError): pass try: for x in yaml_obj['output_ports']: - self.ports['output'][x['name'].format(path = self.path_underscored)] =\ - (x['signal_type'].format(field_type = self.field_type), + self.ports['output'][x['name'].format(**values)] =\ + (x['signal_type'].format(**values), self.total_array_dimensions) except (TypeError, KeyError): pass + # Return RTL with values + return yaml_obj['rtl'].format(**values) + @staticmethod def process_reset_signal(reset_signal): rst = dict() @@ -256,9 +269,16 @@ class Component(): return rst def create_underscored_path(self): - self.owning_addrmap = self.obj.owning_addrmap.inst_name - self.full_path = Component.split_dimensions(self.obj.get_path())[0] - self.path = self.full_path\ - .replace('{}.'.format(self.owning_addrmap), '') + self.owning_addrmap, self.full_path, self.path, self.path_underscored =\ + Component.create_underscored_path_static(self.obj) - 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) diff --git a/srdl2sv/components/field.py b/srdl2sv/components/field.py index ab8ac0f..8522a1d 100644 --- a/srdl2sv/components/field.py +++ b/srdl2sv/components/field.py @@ -39,12 +39,266 @@ class Field(Component): # Print a 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_access_rtl() + self.__add_hw_access() 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): operations = [] @@ -56,20 +310,24 @@ class Field(Component): operations.append(['^', 'assign_xored_operation']) if len(operations) > 0: - self.rtl_header.append( - Field.templ_dict['combo_operation_comment']['rtl'].format( - path = self.path_underscored)) + self.rtl_footer.append( + self.process_yaml( + Field.templ_dict['combo_operation_comment'], + {'path': self.path_underscored} + ) + ) - self.rtl_header = [ - *self.rtl_header, - *[Field.templ_dict[i[1]]['rtl'].format( - path = self.path_underscored, - genvars = self.genvars_str, - op_verilog = i[0]) for i in operations] + self.rtl_footer = [ + *self.rtl_footer, + *[self.process_yaml( + Field.templ_dict[i[1]], + {'path': self.path_underscored, + '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): try: @@ -174,11 +432,10 @@ class Field(Component): '\'x' if not obj.get_property("reset") else\ obj.get_property('reset') - # Define hardware access - self.hw_access = obj.get_property('hw') - self.sw_access = obj.get_property('sw') - self.precedence = obj.get_property('precedence') - + # Define dict that holds all RTL + self.access_rtl = dict() + self.access_rtl['else'] = (["else"], False) + self.access_rtl[''] = ([''], False) def summary(self): # Additional flags that are set @@ -189,14 +446,16 @@ class Field(Component): misc_flags.discard('hw') misc_flags.discard('reset') + precedence = self.obj.get_property('precedence') + # Add comment with summary on field's properties return \ Field.templ_dict['field_comment']['rtl'].format( name = self.name, - hw_access = str(self.hw_access)[11:], - sw_access = str(self.sw_access)[11:], - hw_precedence = '(precedence)' if self.precedence == PrecedenceType.hw else '', - sw_precedence = '(precedence)' if self.precedence == PrecedenceType.sw else '', + hw_access = str(self.obj.get_property('hw'))[11:], + sw_access = str(self.obj.get_property('sw'))[11:], + hw_precedence = '(precedence)' if precedence == PrecedenceType.hw else '', + sw_precedence = '(precedence)' if precedence == PrecedenceType.sw else '', rst_active = self.rst['active'], rst_type = self.rst['type'], 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' self.rtl_header.append( - Field.templ_dict[sense_list]['rtl'].format( - rst_edge = self.rst['edge'], - rst_name = self.rst['name'])) + self.process_yaml( + Field.templ_dict[sense_list], + {'rst_edge': self.rst['edge'], + 'rst_name': self.rst['name']} + ) + ) # Add actual reset line if self.rst['name']: self.rtl_header.append( - Field.templ_dict['rst_field_assign']['rtl'].format( - path = self.path_underscored, - rst_name = self.rst['name'], - rst_negl = "!" if self.rst['active'] == "active_low" else "", - rst_value = self.rst['value'], - genvars = self.genvars_str)) - - self.yaml_signals_to_list(Field.templ_dict['rst_field_assign']) + self.process_yaml( + Field.templ_dict['rst_field_assign'], + {'path': self.path_underscored, + 'rst_name': self.rst['name'], + 'rst_negl': "!" if self.rst['active'] == "active_low" else "", + 'rst_value': self.rst['value'], + 'genvars': self.genvars_str, + 'field_type': self.field_type} + ) + ) self.rtl_header.append("begin") # Add name of actual field to Signal field # 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): # 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\ - self.precedence == PrecedenceType.hw and \ - self.hw_access in (AccessType.rw, AccessType.w) and \ - self.sw_access in (AccessType.rw, AccessType.w): + self.obj.get_property('precedence') == PrecedenceType.hw and \ + self.obj.get_property('hw') in (AccessType.rw, AccessType.w) and \ + 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 "\ "precedence for hardware will render software's "\ diff --git a/srdl2sv/components/regfile.py b/srdl2sv/components/regfile.py index 5f99917..eb6f4d4 100644 --- a/srdl2sv/components/regfile.py +++ b/srdl2sv/components/regfile.py @@ -34,18 +34,23 @@ class RegFile(Component): # Create comment and provide user information about register he/she # is looking at. self.rtl_header = [ - RegFile.templ_dict['regfile_comment']['rtl'].format( - name = obj.inst_name, - dimensions = self.dimensions, - depth = self.depth), - *self.rtl_header + self.process_yaml( + RegFile.templ_dict['regfile_comment'], + {'name': obj.inst_name, + 'dimensions': self.dimensions, + 'depth': self.depth} + ), + *self.rtl_header ] # Create generate block for register and add comment for i in range(self.dimensions-1, -1, -1): self.rtl_footer.append( - RegFile.templ_dict['generate_for_end']['rtl'].format( - dimension = chr(97+i))) + self.process_yaml( + RegFile.templ_dict['generate_for_end'], + {'dimension': chr(97+i)} + ) + ) if self.dimensions and not glbl_settings['generate_active']: self.rtl_header.append("generate") @@ -56,15 +61,18 @@ class RegFile(Component): for i in range(self.dimensions): self.rtl_header.append( - RegFile.templ_dict['generate_for_start']['rtl'].format( - iterator = chr(97+i+self.parents_depths), - limit = self.array_dimensions[i])) + self.process_yaml( + RegFile.templ_dict['generate_for_start'], + {'iterator': chr(97+i+self.parents_depths), + 'limit': self.array_dimensions[i]} + ) + ) # Empty dictionary of register objects # We need a dictionary since it might be required to access the objects later # by name (for example, in case of aliases) self.registers = dict() - self.regfiles = [] + self.regfiles = dict() # Set object to 0 for easy addressing self.obj.current_idx = [0] @@ -79,19 +87,20 @@ class RegFile(Component): elif isinstance(child, node.RegfileNode): self.obj.current_idx = [0] - self.regfiles.append( + self.regfiles[child.inst_name] = \ RegFile( child, self.total_array_dimensions, self.total_stride, config, - glbl_settings)) + glbl_settings) elif isinstance(child, node.RegNode): if child.inst.is_alias: # If the node we found is an alias, we shall not create a # new register. Rather, we bury up the old register and add # additional properties - self.logger.error('Alias registers are not implemented yet!') + self.registers[child.inst.alias_primary_inst.inst_name]\ + .add_alias(child) else: self.obj.current_idx = [0] self.registers[child.inst_name] = \ @@ -104,10 +113,7 @@ class RegFile(Component): # Add registers to children. This must be done in a last step # to account for all possible alias combinations - self.children = [ - *self.regfiles, - *[x for x in self.registers.values()] - ] + self.children = {**self.regfiles, **self.registers} self.logger.info("Done generating all child-regfiles/registers") diff --git a/srdl2sv/components/register.py b/srdl2sv/components/register.py index 26a921d..02db5cd 100644 --- a/srdl2sv/components/register.py +++ b/srdl2sv/components/register.py @@ -1,6 +1,7 @@ import importlib.resources as pkg_resources -import yaml import math +import sys +import yaml from systemrdl import node @@ -25,43 +26,59 @@ class Register(Component): super().__init__(obj, config) # 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 - # 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(): - 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']: - field_obj.sanity_checks() - - self.children.append(field_obj) + self.children[field_range].sanity_checks() + def create_rtl(self): # 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") - 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): self.rtl_header.append( Register.templ_dict['generate_for_start'].format( iterator = chr(97+i+self.parents_depths), 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): self.rtl_footer.append( Register.templ_dict['generate_for_end'].format( dimension = chr(97+i))) - if self.generate_initiated: - glbl_settings['generate_active'] = False - self.rtl_footer.append("endgenerate") + if self.dimensions and not self.generate_active: + self.rtl_footer.append("endgenerate\n") + # 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 self.obj.current_idx = [0] @@ -70,15 +87,18 @@ class Register(Component): else: rw_wire_assign_field = 'rw_wire_assign_1_dim' - self.rtl_header.append( - Register.templ_dict[rw_wire_assign_field]['rtl'].format( - path = self.path_underscored, - addr = self.obj.absolute_address, - genvars = self.genvars_str, - genvars_sum =self.genvars_sum_str, - depth = self.depth)) - - self.yaml_signals_to_list(Register.templ_dict[rw_wire_assign_field]) + [self.rtl_header.append( + self.process_yaml( + Register.templ_dict[rw_wire_assign_field], + {'path': x[0], + 'addr': x[1], + 'alias': '(alias)' if i > 0 else '', + 'genvars': self.genvars_str, + 'genvars_sum': self.genvars_sum_str, + 'depth': self.depth, + 'field_type': self.field_type} + ) + ) for i, x in enumerate(self.alias_names)] # Add wire/register instantiations dict_list = [(key, value) for (key, value) in self.get_signals().items()] @@ -103,31 +123,48 @@ class Register(Component): *self.rtl_header, ] - # 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_alias(self, obj: node.RegNode): + for field in obj.fields(): + # 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])) + 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( self, obj: node.RegNode, parents_dimensions: list, - parents_stride: list): + parents_stride: list, + glbl_settings: dict): # Save object self.obj = obj # Save name + self.obj.current_idx = [0] self.name = obj.inst_name + self.alias_names = [(self.create_underscored_path_static(obj)[3], obj.absolute_address)] # Create full name self.create_underscored_path() + # Gnerate already started? + self.generate_active = glbl_settings['generate_active'] + # Determine dimensions of register if obj.is_array: self.sel_arr = 'array' diff --git a/srdl2sv/components/templates/fields.yaml b/srdl2sv/components/templates/fields.yaml index 9ef54e1..2edf465 100644 --- a/srdl2sv/components/templates/fields.yaml +++ b/srdl2sv/components/templates/fields.yaml @@ -33,7 +33,9 @@ sw_access_byte: begin {path}_q{genvars}[{msb_field}-:{field_w}] <= sw_wr_bus[{msb_bus}-:{bus_w}]; end - + signals: + - name: '{path}_q' + signal_type: '{field_type}' hw_access_we_wel: rtl: |- if ({negl}{path}_hw_wr{genvars}) diff --git a/srdl2sv/components/templates/regs.yaml b/srdl2sv/components/templates/regs.yaml index bd237a8..403102e 100644 --- a/srdl2sv/components/templates/regs.yaml +++ b/srdl2sv/components/templates/regs.yaml @@ -1,34 +1,31 @@ --- rw_wire_assign_1_dim: rtl: | - // Assign register-activation signals - assign {path}_reg_active{genvars} = addr == {addr}; - - assign {path}_sw_wr = {path}_reg_active && r_vld; - assign {path}_sw_rd = {path}_reg_active && w_vld; + // Register-activation for '{path}' {alias} + assign {path}_accss = addr == {addr}; + assign {path}_sw_wr = {path}_accss && r_vld; + assign {path}_sw_rd = {path}_accss && w_vld; signals: - name: '{path}_sw_wr' signal_type: 'logic' - name: '{path}_sw_rd' signal_type: 'logic' - - name: '{path}_reg_active' + - name: '{path}_accss' signal_type: 'logic' input_ports: output_ports: rw_wire_assign_multi_dim: rtl: | - - // Assign register-activation signals - assign {path}_reg_active{genvars} = addr == {addr}+({genvars_sum}); - - assign {path}_sw_wr{genvars} = {path}_reg_active{genvars} && r_vld; - assign {path}_sw_rd{genvars} = {path}_reg_active{genvars} && w_vld; + // Register-activation for '{path}' {alias} + assign {path}_accss{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; signals: - name: '{path}_sw_wr' signal_type: 'logic' - name: '{path}_sw_rd' signal_type: 'logic' - - name: '{path}_reg_active' + - name: '{path}_accss' signal_type: 'logic' input_ports: output_ports: