import math import importlib.resources as pkg_resources import sys import yaml from systemrdl.node import FieldNode, SignalNode from systemrdl.component import Reg, Regfile, Addrmap, Root from systemrdl.rdltypes import PrecedenceType, AccessType, OnReadType, OnWriteType # Local modules from components.component import Component, TypeDef from . import templates class Field(Component): # Save YAML template as class variable templ_dict = yaml.load( pkg_resources.read_text(templates, 'fields.yaml'), Loader=yaml.FullLoader) def __init__( self, obj: FieldNode, array_dimensions: list, config:dict, glbl_settings: dict): super().__init__(obj, config) # Save and/or process important variables self.__process_variables(obj, array_dimensions, glbl_settings) # Determine field types self.__process_fieldtype() ################################################################################## # LIMITATION: # v1.x of the systemrdl-compiler does not support non-homogeneous arrays. # It is planned, however, for v2.0.0 of the compiler. More information # can be found here: https://github.com/SystemRDL/systemrdl-compiler/issues/51 ################################################################################## # Print a summary self.rtl_header.append(self.summary()) # 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 if not self.config['external']: self.__add_always_ff() self.__add_hw_access() self.__add_combo() self.__add_swmod_swacc() self.__add_counter() 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): # Append to list of registers that can write self.writable_by.add(path_wo_field) 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)): msb_bus = 8*(i+1)-1 if i != self.msbyte else obj.msb lsb_bus = 8*i if i != self.lsbyte else obj.inst.lsb 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(msb_bus), 'lsb_bus': str(lsb_bus), 'msb_field': str(msb_bus-obj.inst.lsb), 'lsb_field': str(lsb_bus-obj.inst.lsb), '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)): msb_bus = 8*(i+1)-1 if i != self.msbyte else obj.msb lsb_bus = 8*i if i != self.lsbyte else obj.inst.lsb 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(msb_bus), 'lsb_bus': str(lsb_bus), 'msb_field': str(msb_bus-obj.inst.lsb), 'lsb_field': str(lsb_bus-obj.inst.lsb), '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): # Append to list of registers that can read self.readable_by.add(path_wo_field) # Set onread properties if onread == OnReadType.ruser: self.logger.error("The OnReadType.ruser is not yet supported!") elif onread: 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']] self.path_underscored_vec.append(path_underscored) self.path_wo_field_vec.append(path_wo_field) def __add_counter(self): if self.obj.get_property('counter'): self.logger.debug("Detected counter property") self.rtl_footer.append(Field.templ_dict['counter_comment']['rtl']) # Determine saturation values if isinstance(self.obj.get_property('incrsaturate'), bool): if self.obj.get_property('incrsaturate'): incr_sat_value = 2**self.obj.width-1 overflow_value = incr_sat_value else: incr_sat_value = False overflow_value = 2**self.obj.width-1 else: incr_sat_value = self.obj.get_property('incrsaturate') overflow_value = incr_sat_value if isinstance(self.obj.get_property('decrsaturate'), bool): if self.obj.get_property('decrsaturate'): decr_sat_value = 0 underflow_value = decr_sat_value else: decr_sat_value = False underflow_value = 0 else: decr_sat_value = self.obj.get_property('decrsaturate') underflow_value = decr_sat_value # Determine threshold values if isinstance(self.obj.get_property('incrthreshold'), bool): if self.obj.get_property('incrthreshold'): incr_thr_value = 2**self.obj.width-1 else: incr_thr_value = False else: incr_thr_value = self.obj.get_property('incrthreshold') if isinstance(self.obj.get_property('decrthreshold'), bool): if self.obj.get_property('decrthreshold'): decr_thr_value = 2**self.obj.width-1 else: decr_thr_value = False else: decr_thr_value = self.obj.get_property('decrthreshold') # Determine with what value the counter is incremented # According to the spec, the incrvalue/decrvalue default to '1' obj_incr_value = self.obj.get_property('incrvalue') obj_decr_value = self.obj.get_property('decrvalue') obj_incr_width = self.obj.get_property('incrwidth') obj_decr_width = self.obj.get_property('decrwidth') incr_width_input = False if obj_incr_value == 0: incr_value = 0 incr_width = 1 elif obj_incr_value is None: # Increment value is not set. Check if incrwidth is set and use # that is applicable if obj_incr_width: incr_value = False incr_width = obj_incr_width incr_width_input = True # Doesn't return RTL, only adds input port self.process_yaml( Field.templ_dict['counter_incr_val_input'], {'path': self.path_underscored, 'genvars': self.genvars_str, 'incr_width': incr_width-1 } ) else: # Otherwise, use default value according to LRM incr_value = '1' incr_width = 1 elif isinstance(obj_incr_value, int): # An explicit width is set for the incr_val incr_value = str(obj_incr_value) incr_width = math.floor(math.log2(obj_incr_value)+1) if obj_incr_width: self.logger.error( "The 'incrwidth' and 'incrvalue' properties are both "\ "defined. This is not legal and the incrwidth property "\ "will be ignored!") else: incr_value = self.get_signal_name(obj_incr_value) incr_width = obj_incr_value.width if obj_incr_value.width > self.obj.width: self.logger.error( "Width of 'incr_value' signal '{}' is wider than current "\ "counter field. This could potentially cause ugly errors.".format( obj_incr_value.get_path())) if obj_incr_width: self.logger.error( "The 'incrwidth' and 'incrvalue' properties are both "\ "defined. This is not legal and the incrwidth property "\ "will be ignored!") # If no input is defined for the increment value, define # an internal signal. It is possible that this is tied to 0. if not incr_width_input: self.rtl_footer.append( self.process_yaml( Field.templ_dict['counter_internal_incr_val_signal'], {'path': self.path_underscored, 'genvars': self.genvars_str, 'incr_width': incr_width-1, 'incr_value': incr_value, } ) ) # Handle decrement value decr_width_input = False if obj_decr_value == 0: decr_value = 0 decr_width = 1 elif obj_incr_value is None: # Decrement value is not set. Check if decrwidth is set and use # that is applicable if obj_decr_width: decr_value = False decr_width = obj_decr_width decr_width_input = True # Doesn't return RTL, only adds input port self.process_yaml( Field.templ_dict['counter_decr_val_input'], {'path': self.path_underscored, 'genvars': self.genvars_str, 'decr_width': decr_width-1 } ) else: # Otherwise, use default value according to LRM decr_value = '1' decr_width = 1 elif isinstance(obj_decr_value, int): # An explicit width is set for the decr_val decr_value = str(obj_decr_value) decr_width = math.floor(math.log2(obj_decr_value)+1) if obj_decr_width: self.logger.error( "The 'decrwidth' and 'decrvalue' properties are both "\ "defined. This is not legal and the decrwidth property "\ "will be ignored!") else: decr_value = self.get_signal_name(obj_decr_value) decr_width = obj_decr_value.width if obj_decr_value.width > self.obj.width: self.logger.error( "Width of 'decr_value' signal '{}' is wider than current "\ "counter field. This could potentially cause ugly errors.".format( obj_decr_value.get_path())) if obj_decr_width: self.logger.error( "The 'decrwidth' and 'decrvalue' properties are both "\ "defined. This is not legal and the decrwidth property "\ "will be ignored!") # If no input is defined for the decrement value, define # an internal signal. It is possible that this is tied to 0. if not decr_width_input: self.rtl_footer.append( self.process_yaml( Field.templ_dict['counter_internal_decr_val_signal'], {'path': self.path_underscored, 'genvars': self.genvars_str, 'decr_width': decr_width-1, 'decr_value': decr_value, } ) ) # Handle the increment/decrement signals. # If the increment or decrement signal is not set, use an input # if the decrement value is bigger than 0 if not incr_value and not decr_value: self.logger.fatal("Illegal counter configuration! Both 'incr_value' "\ "and 'decr_value' are forced to 0. If you intended "\ "to use 'incr_width' or 'decr_width', simply don't "\ "force 'incr_value' or 'decr_value' to any value.") sys.exit(1) if incr_value: incr = self.obj.get_property('incr') if not incr: # Will only add input port but not return any RTL self.process_yaml( Field.templ_dict['counter_incr_input'], {'path': self.path_underscored} ) else: self.rtl_footer.append( self.process_yaml( Field.templ_dict['counter_internal_incr_signal'], {'path': self.path_underscored, 'genvars': self.genvars_str, 'incr': self.get_signal_name(incr) } ) ) try: if incr.width > 0: self.logger.error("Increment signal '{}' is wider than 1-bit. "\ "This might result in unwanted behavior and "\ "will also cause Lint-errors.".format( incr.inst_name)) except AttributeError: # 'PropRef_overflow' object has no attribute 'width' pass else: # Tie signal to 0 self.rtl_footer.append( self.process_yaml( Field.templ_dict['counter_internal_incr_signal'], {'path': self.path_underscored, 'genvars': self.genvars_str, 'incr': '0' } ) ) if decr_value: decr = self.obj.get_property('decr') if not self.obj.get_property('decr'): # Will only add input port but not return any RTL self.process_yaml( Field.templ_dict['counter_decr_input'], {'path': self.path_underscored} ) else: self.rtl_footer.append( self.process_yaml( Field.templ_dict['counter_internal_decr_signal'], {'path': self.path_underscored, 'genvars': self.genvars_str, 'decr': self.get_signal_name(decr) } ) ) try: if decr.width > 0: self.logger.error("Decrement signal '{}' is wider than 1-bit. "\ "This might result in unwanted behavior and "\ "will also cause Lint-errors.".format( decr.inst_name)) except AttributeError: # 'PropRef_underflow' object has no attribute 'width' pass else: # Tie signal to 0 self.rtl_footer.append( self.process_yaml( Field.templ_dict['counter_internal_decr_signal'], {'path': self.path_underscored, 'genvars': self.genvars_str, 'decr': '0' } ) ) # Handle saturation signals if not incr_sat_value: self.rtl_footer.append( self.process_yaml( Field.templ_dict['counter_incr_sat_tied'], {'path': self.path_underscored, 'genvars': self.genvars_str, } ) ) else: self.rtl_footer.append( self.process_yaml( Field.templ_dict['counter_incr_sat'], {'path': self.path_underscored, 'genvars': self.genvars_str, 'incr_width': incr_width, 'decr_width': decr_width, 'sat_value': incr_sat_value, } ) ) if not decr_sat_value: self.rtl_footer.append( self.process_yaml( Field.templ_dict['counter_decr_sat_tied'], {'path': self.path_underscored, 'genvars': self.genvars_str, } ) ) else: self.rtl_footer.append( self.process_yaml( Field.templ_dict['counter_decr_sat'], {'path': self.path_underscored, 'genvars': self.genvars_str, 'incr_width': incr_width, 'decr_width': decr_width, 'sat_value': decr_sat_value, } ) ) # Handle threshold values if incr_thr_value or decr_thr_value: self.rtl_footer.append(Field.templ_dict['counter_thr_comment']['rtl']) if incr_thr_value: self.rtl_footer.append( self.process_yaml( Field.templ_dict['counter_incr_thr'], {'path': self.path_underscored, 'genvars': self.genvars_str, 'incr_width': incr_width, 'decr_width': decr_width, 'thr_value': incr_thr_value, } ) ) if decr_thr_value: self.rtl_footer.append( self.process_yaml( Field.templ_dict['counter_decr_thr'], {'path': self.path_underscored, 'genvars': self.genvars_str, 'incr_width': incr_width, 'decr_width': decr_width, 'thr_value': decr_thr_value, } ) ) # Handle overflow & underflow signals if self.obj.get_property('overflow'): self.rtl_footer.append( self.process_yaml( Field.templ_dict['counter_overflow'], {'path': self.path_underscored, 'genvars': self.genvars_str, 'incr_width': incr_width, 'decr_width': decr_width, 'overflow_value': overflow_value, } ) ) if self.obj.get_property('underflow'): self.rtl_footer.append( self.process_yaml( Field.templ_dict['counter_underflow'], {'path': self.path_underscored, 'genvars': self.genvars_str, 'incr_width': incr_width, 'decr_width': decr_width, 'underflow_value': underflow_value, } ) ) # Implement actual counter logic self.rtl_footer.append( self.process_yaml( Field.templ_dict['counter'], {'path': self.path_underscored, 'genvars': self.genvars_str, } ) ) def __add_swmod_swacc(self): if self.obj.get_property('swmod'): self.logger.debug("Field has swmod property") swmod_assigns = list() # Check if read side-effects are defined. if self.obj.get_property('onread'): swmod_assigns.append( self.process_yaml( Field.templ_dict['swmod_assign'], {'path': self.path_underscored, 'path_wo_field': self.path_wo_field, 'genvars': self.genvars_str, 'rd_wr': 'rd', 'msbyte': self.msbyte, 'lsbyte': self.lsbyte, 'swmod_assigns': '\n'.join(swmod_assigns) } ) ) # Check if SW has write access to the field if self.obj.get_property('sw') in (AccessType.rw, AccessType.w): swmod_assigns.append( self.process_yaml( Field.templ_dict['swmod_assign'], {'path': self.path_underscored, 'path_wo_field': self.path_wo_field, 'genvars': self.genvars_str, 'rd_wr': 'wr', 'msbyte': self.msbyte, 'lsbyte': self.lsbyte, 'swmod_assigns': '\n'.join(swmod_assigns) } ) ) swmod_props = self.process_yaml( Field.templ_dict['swmod_always_comb'], {'path': self.path_underscored, 'genvars': self.genvars_str, 'swmod_assigns': '\n'.join(swmod_assigns) } ) if not swmod_assigns: self.logger.warning("Field has swmod property but the field is never "\ "modified by software.") else: swmod_props = '' if self.obj.get_property('swacc') and \ self.obj.get_property('sw') in (AccessType.rw, AccessType.r): self.logger.debug("Field has swacc property") swacc_props = self.process_yaml( Field.templ_dict['swacc_assign'], {'path': self.path_underscored, 'path_wo_field': self.path_wo_field, 'genvars': self.genvars_str, 'msbyte': self.msbyte, 'lsbyte': self.lsbyte, } ) elif self.obj.get_property('swacc'): self.logger.warning("Field has swacc property but the field is never "\ "accessed by software.") swacc_props = '' else: swacc_props = '' self.rtl_footer = [*self.rtl_footer, swmod_props, swacc_props] def __add_hw_access(self): # Mutually exclusive. systemrdl-compiler performs check for this enable_mask_negl = '' enable_mask = False if self.obj.get_property('hwenable'): enable_mask = self.obj.get_property('hwenable') elif self.obj.get_property('hwmask'): enable_mask = self.obj.get_property('hwmask') enable_mask_negl = '!' if enable_mask: enable_mask_start_rtl = \ self.process_yaml( Field.templ_dict['hw_enable_mask_start'], {'signal': self.get_signal_name(enable_mask), 'width': self.obj.width, 'negl': enable_mask_negl} ) enable_mask_end_rtl = \ self.process_yaml( Field.templ_dict['hw_enable_mask_end'], {'width': self.obj.width} ) enable_mask_idx = '[idx]' else: enable_mask_start_rtl = '<>' enable_mask_end_rtl = '<>' enable_mask_idx = '' # Define hardware access (if applicable) if self.obj.get_property('counter'): self.access_rtl['hw_write'] = ([ self.process_yaml( Field.templ_dict['hw_access_counter'], {'path': self.path_underscored, 'genvars': self.genvars_str, 'field_type': self.field_type, 'enable_mask_start': enable_mask_start_rtl, 'enable_mask_end': enable_mask_end_rtl, 'idx': enable_mask_idx} ) ], False) elif 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 if self.obj.get_property('next'): # 'next' property is used self.logger.debug("Found property 'next'") assignment = self.get_signal_name(self.obj.get_property('next')) if self.we_or_wel: self.logger.info("This field has a 'we' or 'wel' property and " "uses the 'next' property. Make sure this is " "is intentional.") else: # No special property. Assign input to register assignment = \ self.process_yaml( Field.templ_dict['hw_access_field__assignment__input'], {'path': self.path_underscored, 'genvars': self.genvars_str, 'idx': enable_mask_idx, 'field_type': self.field_type} ) self.access_rtl['hw_write'][0].append( self.process_yaml( Field.templ_dict['hw_access_field'], {'path': self.path_underscored, 'genvars': self.genvars_str, 'enable_mask_start': enable_mask_start_rtl, 'enable_mask_end': enable_mask_end_rtl, 'assignment': assignment, 'idx': enable_mask_idx, 'field_type': self.field_type} ) ) else: self.access_rtl['hw_write'] = ([], False) # Check if the hwset or hwclr option is set if self.obj.get_property('hwset'): self.access_rtl['hw_setclr'] = ([ self.process_yaml( Field.templ_dict['hw_access_hwset'], {'path': self.path_underscored, 'genvars': self.genvars_str, 'enable_mask_start': enable_mask_start_rtl, 'enable_mask_end': enable_mask_end_rtl, 'idx': enable_mask_idx, 'constant': "{{{}{{1'b1}}}}".format(self.obj.width) if not enable_mask else "1'b1" } ) ], False) elif self.obj.get_property('hwclr'): self.access_rtl['hw_setclr'] = ([ self.process_yaml( Field.templ_dict['hw_access_hwclr'], {'path': self.path_underscored, 'genvars': self.genvars_str, 'enable_mask_start': enable_mask_start_rtl, 'enable_mask_end': enable_mask_end_rtl, 'idx': enable_mask_idx, 'constant': "{{{}{{1'b0}}}}".format(self.obj.width) if not enable_mask else "1'b0" } ) ], False) else: self.access_rtl['hw_setclr'] = ([], 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_external_rtl(self): if self.obj.get_property('sw') in (AccessType.rw, AccessType.w): for i, alias in enumerate(self.path_underscored_vec): # Create bit-wise mask so that outside logic knows what # bits it may change mask = [] for j, byte_idx in enumerate(range(self.msbyte, self.lsbyte-1, -1)): if byte_idx == self.lsbyte: width = (self.lsbyte+1)*8 - self.lsb elif byte_idx == self.msbyte: width = 8 - ((self.msbyte+1)*8-1 - self.msb) else: width = 8 mask.append( Field.templ_dict['external_wr_mask_segment']['rtl'].format( idx = byte_idx, width = width) ) self.rtl_footer.append(self.process_yaml( Field.templ_dict['external_wr_assignments'], {'path': alias, 'path_wo_field': self.path_wo_field_vec[i], 'genvars': self.genvars_str, 'msb_bus': self.msb, 'lsb_bus': self.lsb, 'mask': ','.join(mask), 'width': self.obj.width-1, 'field_type': self.field_type } )) if self.obj.get_property('sw') in (AccessType.rw, AccessType.r): for i, alias in enumerate(self.path_underscored_vec): self.rtl_footer.append(self.process_yaml( Field.templ_dict['external_rd_assignments'], {'path': alias, 'path_wo_field': self.path_wo_field_vec[i], 'genvars': self.genvars_str, 'field_type': self.field_type } )) def create_internal_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', 'hw_setclr', 'singlepulse' ] else: order_list = [ 'hw_write', 'hw_setclr', '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 = [] if self.obj.get_property('anded'): operations.append(['&', 'assign_anded_operation']) if self.obj.get_property('ored'): operations.append(['|', 'assign_ored_operation']) if self.obj.get_property('xored'): operations.append(['^', 'assign_xored_operation']) if len(operations) > 0: self.rtl_footer.append( self.process_yaml( Field.templ_dict['combo_operation_comment'], {'path': self.path_underscored} ) ) 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] ] def __process_fieldtype(self): try: if not self.config['enums']: raise AttributeError enum = self.obj.get_property('encode') # Rules for scope: # - Regfiles or addrmaps have packages # - An enum that is not defined within a register will go into the package # of the first addrmap or regfile that is found when iterating through # the parents # - Regfiles don't need to be unique in a design. Therefore, the packages of # regfiles shall be prepended by the addrmap name. # - When the enum is defined in a register, that register will be prepended # to the name of that enum. # # This procedure is expensive, but None.parent() will not work and therefore # kill the try block in most cases parent_scope = enum.get_parent_scope() self.logger.debug("Starting to parse '{}'".format(enum)) if isinstance(parent_scope, Reg): enum_name = '__'.join([enum.get_scope_path().split('::')[-1], enum.__name__]) parent_scope = parent_scope.parent_scope else: enum_name = enum.__name__ path = [] # Open up all parent scopes and append it to scope list while 1: if isinstance(parent_scope, Regfile): path.append(parent_scope._scope_name) # That's a lot of parent_scope's... parent_scope = parent_scope.parent_scope else: path.append(self.owning_addrmap) break # Create string. Reverse list so that order starts at addrmap scope = '__'.join(reversed(path)) # Create internal NamedTuple with information on Enum self.typedefs[enum_name] = TypeDef ( scope=scope, width=self.obj.width, members= [(x.name, x.value) for x in self.obj.get_property('encode')] ) # Save name of object self.field_type =\ '::'.join(['_'.join([scope, 'pkg']), enum_name]) self.logger.info("Parsed enum '{}'".format(enum_name)) except AttributeError: # In case of an AttributeError, the encode property is None. Hence, # the field has a simple width self.field_type = 'logic [{}:0]'.format(self.obj.width-1) def __process_variables(self, obj: FieldNode, array_dimensions: list, glbl_settings: dict): # Create full name self.path_wo_field = '__'.join(self.path.split('.', -1)[0:-1]) self.path_underscored_vec = [] self.path_wo_field_vec = [] # Save dimensions of unpacked dimension self.array_dimensions = array_dimensions self.total_array_dimensions = array_dimensions self.total_dimensions = len(self.total_array_dimensions) # Calculate how many genvars shall be added genvars = ['[{}]'.format(chr(97+i)) for i in range(len(array_dimensions))] self.genvars_str = ''.join(genvars) # Write enable self.we_or_wel = self.obj.get_property('we') or self.obj.get_property('wel') # Save byte boundaries self.lsbyte = math.floor(obj.inst.lsb / 8) self.msbyte = math.floor(obj.inst.msb / 8) self.msb = obj.inst.msb self.lsb = obj.inst.lsb # Set that tells which hierarchies can read/write this field self.readable_by = set() self.writable_by = set() # Determine resets. This includes checking for async/sync resets, # the reset value, and whether the field actually has a reset self.rst = Field.process_reset_signal( obj.get_property("resetsignal")) if self.rst['name']: self.resets.add(self.rst['name']) # Value of reset must always be determined on field level self.rst['value'] = \ '\'x' if not obj.get_property("reset") else\ obj.get_property('reset') # 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 misc_flags = set(self.obj.list_properties()) # Remove some flags that are not interesting # or that are listed elsewhere 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.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 '-', external = self.config['external'], lsb = self.obj.lsb, msb = self.obj.msb, path_wo_field = self.path_wo_field) def __add_always_ff(self): # Handle always_ff sense_list = 'sense_list_rst' if self.rst['async'] else 'sense_list_no_rst' self.rtl_header.append( 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( 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 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.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 "\ "write property useless since hardware will "\ "write every cycle.") # TODO: Counter & hw=r shouldn't work