diff --git a/srdl2sv/components/addrmap.py b/srdl2sv/components/addrmap.py index 08ef0c8..4c49860 100644 --- a/srdl2sv/components/addrmap.py +++ b/srdl2sv/components/addrmap.py @@ -2,11 +2,12 @@ import re import importlib.resources as pkg_resources import yaml -from systemrdl import RDLCompiler, RDLCompileError, RDLWalker, RDLListener, node +from systemrdl import node from systemrdl.node import FieldNode # Local packages from components.component import Component +from components.regfile import RegFile from components.register import Register from . import templates @@ -18,17 +19,12 @@ class AddrMap(Component): Loader=yaml.FullLoader) def __init__(self, obj: node.RootNode, config: dict): - super().__init__() - - # Save and/or process important variables - self.__process_variables(obj) + super().__init__(obj, config) # Create logger object self.create_logger(self.path, config) self.logger.debug('Starting to process addrmap') - template = pkg_resources.read_text(templates, 'addrmap.sv') - # Check if global resets are defined glbl_settings = dict() @@ -48,7 +44,7 @@ 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.logger.error('Regfiles are not implemented yet!') + self.children.append(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 @@ -57,15 +53,19 @@ class AddrMap(Component): self.logger.error('Alias registers are not implemented yet!') else: self.registers[child.inst_name] = \ - Register(child, config, glbl_settings) + Register(child, [], [], config, glbl_settings) - # Add regfiles and registers to children - self.children = [x for x in self.registers.values()] + # Add registers to children. This must be done in a last step + # to account for all possible alias combinations + self.children = [ + *self.children, + *[x for x in self.registers.values()] + ] self.logger.info("Done generating all child-regfiles/registers") # Start assembling addrmap module - self.logger.info("Starting to assemble input/output/inout ports") + self.logger.info("Starting to assemble input & output ports") # Reset ports reset_ports_rtl = [ @@ -124,25 +124,11 @@ class AddrMap(Component): self.rtl_header.append( AddrMap.templ_dict['module_declaration'].format( - name = obj.inst_name, + name = self.name, resets = '\n'.join(reset_ports_rtl), inputs = '\n'.join(input_ports_rtl), outputs = '\n'.join(output_ports_rtl))) - def __process_variables(self, obj: node.RootNode): - # Save object - self.obj = obj - - # Create full name - self.owning_addrmap = obj.owning_addrmap.inst_name - self.path = obj.get_path()\ - .replace('[]', '')\ - .replace('{}.'.format(self.owning_addrmap), '') - - self.path_underscored = self.path.replace('.', '_') - - self.name = obj.inst_name - def __process_global_resets(self): field_reset_list = \ [x for x in self.obj.signals() if x.get_property('field_reset')] diff --git a/srdl2sv/components/component.py b/srdl2sv/components/component.py index ad09702..49f387c 100644 --- a/srdl2sv/components/component.py +++ b/srdl2sv/components/component.py @@ -12,7 +12,7 @@ class TypeDef(NamedTuple): members: tuple class Component(): - def __init__(self): + def __init__(self, obj, config): self.rtl_header = [] self.rtl_footer = [] self.children = [] @@ -24,6 +24,19 @@ class Component(): self.ports['output'] = dict() self.field_type = '' + # Save object + self.obj = obj + + # Save name + self.name = obj.inst_name + + # Create path + self.create_underscored_path() + + # Create logger object + self.create_logger("{}.{}".format(self.owning_addrmap, self.path), config) + self.logger.debug('Starting to process register "{}"'.format(obj.inst_name)) + def create_logger(self, name: str, config: dict): self.logger = create_logger( "{}".format(name), @@ -121,7 +134,7 @@ class Component(): @staticmethod def split_dimensions(path: str): - re_dimensions = re.compile('(\[[^]]\])') + re_dimensions = re.compile('(\[[^]]*\])') new_path = re_dimensions.sub('', path) return (new_path, ''.join(re_dimensions.findall(path))) @@ -159,7 +172,7 @@ class Component(): 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.array_dimensions) + self.total_array_dimensions) except (TypeError, KeyError): pass @@ -167,7 +180,7 @@ class Component(): 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.array_dimensions) + self.total_array_dimensions) except (TypeError, KeyError): pass @@ -175,7 +188,7 @@ class Component(): 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.array_dimensions) + self.total_array_dimensions) except (TypeError, KeyError): pass @@ -204,3 +217,11 @@ class Component(): rst['type'] = "-" 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.path_underscored = self.path.replace('.', '__') diff --git a/srdl2sv/components/field.py b/srdl2sv/components/field.py index 2300f2e..83634fc 100644 --- a/srdl2sv/components/field.py +++ b/srdl2sv/components/field.py @@ -16,16 +16,17 @@ class Field(Component): 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__() + 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) - # Create logger object - self.create_logger("{}.{}".format(self.owning_addrmap, self.path), config) - self.logger.debug('Starting to process field "{}"'.format(obj.inst_name)) - # Determine field types self.__process_fieldtype() @@ -137,21 +138,12 @@ class Field(Component): self.field_type = 'logic' def __process_variables(self, obj: FieldNode, array_dimensions: list, glbl_settings: dict): - # Save object - self.obj = obj - # Create full name - self.owning_addrmap = obj.owning_addrmap.inst_name - self.full_path = obj.get_path().replace('[]', '') - self.path = self.full_path\ - .replace('[]', '')\ - .replace('{}.'.format(self.owning_addrmap), '') - - self.path_underscored = self.path.replace('.', '_') self.path_wo_field = '.'.join(self.path.split('.', -1)[0:-1]) # Save dimensions of unpacked dimension self.array_dimensions = array_dimensions + self.total_array_dimensions = array_dimensions # Calculate how many genvars shall be added genvars = ['[{}]'.format(chr(97+i)) for i in range(len(array_dimensions))] @@ -170,7 +162,7 @@ class Field(Component): reset_signal = obj.get_property("resetsignal") - if (reset_signal): + if reset_signal: self.rst = Field.process_reset_signal(reset_signal) else: # Only use global reset (if present) if no local reset is set @@ -180,7 +172,7 @@ class Field(Component): # Value of reset must always be determined on field level self.rst['value'] = \ - '\'x' if obj.get_property("reset") == None else\ + '\'x' if not obj.get_property("reset") else\ obj.get_property('reset') # Define hardware access @@ -201,7 +193,7 @@ class Field(Component): # Add comment with summary on field's properties return \ Field.templ_dict['field_comment']['rtl'].format( - name = self.obj.inst_name, + 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 '', diff --git a/srdl2sv/components/regfile.py b/srdl2sv/components/regfile.py new file mode 100644 index 0000000..aa686e7 --- /dev/null +++ b/srdl2sv/components/regfile.py @@ -0,0 +1,142 @@ +import re +import importlib.resources as pkg_resources +import sys +import math +import yaml + +from systemrdl import node +from systemrdl.node import FieldNode + +# Local packages +from components.component import Component +from components.register import Register +from . import templates + + +class RegFile(Component): + # Save YAML template as class variable + templ_dict = yaml.load( + pkg_resources.read_text(templates, 'regfile.yaml'), + Loader=yaml.FullLoader) + + def __init__( + self, + obj: node.RegfileNode, + parents_dimensions: list, + parents_stride: list, + config: dict, + glbl_settings: dict): + super().__init__(obj, config) + + # Save and/or process important variables + self.__process_variables(obj, parents_dimensions, parents_stride) + + # 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 + ] + + # Create generate block for register and add comment + if self.dimensions: + self.rtl_header.append("generate") + + 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])) + + # 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() + + # Set object to 0 for easy addressing + self.obj.current_idx = [0] + + # Traverse through children + for child in obj.children(): + if isinstance(child, node.AddrmapNode): + self.logger.fatal('Instantiating addrmaps within regfiles is not '\ + 'supported. Addrmaps shall be instantiated at the '\ + 'top-level of other addrmaps') + sys.exit(1) + elif isinstance(child, node.RegfileNode): + self.obj.current_idx = [0] + + self.children.append( + RegFile( + child, + self.total_array_dimensions, + self.total_stride, + 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!') + else: + self.obj.current_idx = [0] + self.registers[child.inst_name] = \ + Register( + child, + self.total_array_dimensions, + self.total_stride, + 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.children, + *[x for x in self.registers.values()] + ] + + self.logger.info("Done generating all child-regfiles/registers") + + def __process_variables(self, + obj: node.RegfileNode, + parents_dimensions: list, + parents_stride: list): + + # Determine dimensions of register + if obj.is_array: + self.sel_arr = 'array' + self.total_array_dimensions = [*parents_dimensions, *self.obj.array_dimensions] + self.array_dimensions = self.obj.array_dimensions + + # Merge parent's stride with stride of this regfile. Before doing so, the + # respective stride of the different dimensions shall be calculated + self.total_stride = [ + *parents_stride, + *[math.prod(self.array_dimensions[i+1:]) + *self.obj.array_stride + for i, _ in enumerate(self.array_dimensions)] + ] + else: + self.sel_arr = 'single' + self.total_array_dimensions = parents_dimensions + self.array_dimensions = [] + self.total_stride = parents_stride + + # How many dimensions were already part of some higher up hierarchy? + self.parents_depths = len(parents_dimensions) + + self.total_depth = '[{}]'.format(']['.join(f"{i}" for i in self.total_array_dimensions)) + self.total_dimensions = len(self.total_array_dimensions) + + self.depth = '[{}]'.format(']['.join(f"{i}" for i in self.array_dimensions)) + self.dimensions = len(self.array_dimensions) + + # Calculate how many genvars shall be added + genvars = ['[{}]'.format(chr(97+i)) for i in range(self.dimensions)] + self.genvars_str = ''.join(genvars) + + diff --git a/srdl2sv/components/register.py b/srdl2sv/components/register.py index 543d012..9077a25 100644 --- a/srdl2sv/components/register.py +++ b/srdl2sv/components/register.py @@ -1,5 +1,6 @@ import importlib.resources as pkg_resources import yaml +import math from systemrdl import node @@ -14,20 +15,22 @@ class Register(Component): pkg_resources.read_text(templates, 'regs.yaml'), Loader=yaml.FullLoader) - def __init__(self, obj: node.RootNode, config: dict, glbl_settings: dict): - super().__init__() + def __init__( + self, + obj: node.RegNode, + parents_dimensions: list, + parents_stride: list, + config: dict, + glbl_settings: dict): + super().__init__(obj, config) # Save and/or process important variables - self.__process_variables(obj) - - # 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.__process_variables(obj, parents_dimensions, parents_stride) # Create RTL for fields # Fields should be in order in RTL,therefore, use list for field in obj.fields(): - field_obj = Field(field, self.array_dimensions, config, glbl_settings) + field_obj = Field(field, self.total_array_dimensions, config, glbl_settings) if not config['disable_sanity']: field_obj.sanity_checks() @@ -35,11 +38,13 @@ class Register(Component): self.children.append(field_obj) # Create generate block for register and add comment - self.rtl_header.append("generate") + if self.dimensions: + self.rtl_header.append("generate") + for i in range(self.dimensions): self.rtl_header.append( Register.templ_dict['generate_for_start'].format( - iterator = chr(97+i), + iterator = chr(97+i+self.parents_depths), limit = self.array_dimensions[i])) @@ -49,6 +54,9 @@ class Register(Component): Register.templ_dict['generate_for_end'].format( dimension = chr(97+i))) + if self.dimensions: + self.rtl_footer.append("endgenerate") + # Assign variables from bus self.obj.current_idx = [0] @@ -59,11 +67,10 @@ class Register(Component): self.rtl_header.append( Register.templ_dict[rw_wire_assign_field]['rtl'].format( - path = self.path, + path = self.path_underscored, addr = self.obj.absolute_address, genvars = self.genvars_str, genvars_sum =self.genvars_sum_str, - stride = self.obj.array_stride, depth = self.depth)) self.yaml_signals_to_list(Register.templ_dict[rw_wire_assign_field]) @@ -102,7 +109,11 @@ class Register(Component): ] - def __process_variables(self, obj: node.RootNode): + def __process_variables( + self, + obj: node.RegNode, + parents_dimensions: list, + parents_stride: list): # Save object self.obj = obj @@ -110,37 +121,62 @@ class Register(Component): self.name = obj.inst_name # Create full name - self.owning_addrmap = obj.owning_addrmap.inst_name - self.path = obj.get_path()\ - .replace('[]', '')\ - .replace('{}.'.format(self.owning_addrmap), '') - - self.path_underscored = self.path.replace('.', '_') + self.create_underscored_path() # Determine dimensions of register if obj.is_array: self.sel_arr = 'array' + self.total_array_dimensions = [*parents_dimensions, *self.obj.array_dimensions] self.array_dimensions = self.obj.array_dimensions + + # Merge parent's stride with stride of this regfile. Before doing so, the + # respective stride of the different dimensions shall be calculated + self.total_stride = [ + *parents_stride, + *[math.prod(self.array_dimensions[i+1:]) + *self.obj.array_stride + for i, _ in enumerate(self.array_dimensions)] + ] else: self.sel_arr = 'single' + self.total_array_dimensions = parents_dimensions self.array_dimensions = [] + self.total_stride = self.obj.array_stride + + # How many dimensions were already part of some higher up hierarchy? + self.parents_depths = len(parents_dimensions) + + self.total_depth = '[{}]'.format(']['.join(f"{i}" for i in self.total_array_dimensions)) + self.total_dimensions = len(self.total_array_dimensions) self.depth = '[{}]'.format(']['.join(f"{i}" for i in self.array_dimensions)) self.dimensions = len(self.array_dimensions) # Calculate how many genvars shall be added - genvars = ['[{}]'.format(chr(97+i)) for i in range(self.dimensions)] + genvars = ['[{}]'.format(chr(97+i)) for i in range(self.total_dimensions)] self.genvars_str = ''.join(genvars) # Determine value to compare address with genvars_sum = [] - for i in range(self.dimensions): - if i != 0: - genvars_sum.append("+") - genvars_sum.append("*".join(map(str,self.array_dimensions[self.dimensions-i:]))) + try: + for i, stride in enumerate(self.total_stride): + genvars_sum.append(chr(97+i)) genvars_sum.append("*") + genvars_sum.append(str(stride)) + genvars_sum.append("+") - genvars_sum.append(chr(97+self.dimensions-1-i)) + genvars_sum.pop() + + self.logger.debug( + "Multidimensional with dimensions '{}' and stride '{}'".format( + self.total_array_dimensions, + self.total_stride)) + except TypeError: + self.logger.debug( + "Caught expected TypeError because self.total_stride is empty") + except IndexError: + self.logger.debug( + "Caugt expected IndexError because genvars_sum is empty") self.genvars_sum_str = ''.join(genvars_sum) diff --git a/srdl2sv/components/templates/addrmap.sv b/srdl2sv/components/templates/addrmap.sv deleted file mode 100644 index e69de29..0000000 diff --git a/srdl2sv/components/templates/fields.yaml b/srdl2sv/components/templates/fields.yaml index 40b0a49..7c05b39 100644 --- a/srdl2sv/components/templates/fields.yaml +++ b/srdl2sv/components/templates/fields.yaml @@ -142,6 +142,7 @@ singlepulse: end out_port_assign: rtl: |- + // Connect register to hardware output port assign {path}_r{genvars} = {path}_q{genvars}; output_ports: diff --git a/srdl2sv/components/templates/regfile.yaml b/srdl2sv/components/templates/regfile.yaml new file mode 100644 index 0000000..2f89736 --- /dev/null +++ b/srdl2sv/components/templates/regfile.yaml @@ -0,0 +1,17 @@ +--- +regfile_comment: + rtl: |- + /******************************************************************* + ******************************************************************* + * REGFILE : {name} + * DIMENSION : {dimensions} + * DEPTHS (per dimension): {depth} + ******************************************************************* + *******************************************************************/ +generate_for_start: + rtl: |- + for ({iterator} = 0; {iterator} < {limit}; {iterator}++) + begin +generate_for_end: + rtl: |- + end // of for loop with iterator {dimension} diff --git a/srdl2sv/components/templates/regs.yaml b/srdl2sv/components/templates/regs.yaml index 4c4b4dd..bd237a8 100644 --- a/srdl2sv/components/templates/regs.yaml +++ b/srdl2sv/components/templates/regs.yaml @@ -4,8 +4,8 @@ rw_wire_assign_1_dim: // 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; + assign {path}_sw_wr = {path}_reg_active && r_vld; + assign {path}_sw_rd = {path}_reg_active && w_vld; signals: - name: '{path}_sw_wr' signal_type: 'logic' @@ -19,10 +19,10 @@ rw_wire_assign_multi_dim: rtl: | // Assign register-activation signals - assign {path}_reg_active{genvars} = addr == ({addr}+({genvars_sum})*{stride}); + 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; + assign {path}_sw_wr{genvars} = {path}_reg_active{genvars} && r_vld; + assign {path}_sw_rd{genvars} = {path}_reg_active{genvars} && w_vld; signals: - name: '{path}_sw_wr' signal_type: 'logic'