From b7c1a12179c86eda2d9505e488ad8ebba821d4a5 Mon Sep 17 00:00:00 2001 From: Dennis Date: Mon, 24 May 2021 11:41:45 +0200 Subject: [PATCH] Add support for enumeration encoding Fields that are encoded as enumerations are now recognized by the application. All relevant information will be saved in the Field Object and the variables and I/O list will be generated accordingly. This commit also adds dynamic padding of the I/O and variable lists. Still lacking is the automatic generation of SV packages. --- srdl2sv/components/addrmap.py | 25 ++++++- srdl2sv/components/component.py | 9 +-- srdl2sv/components/field.py | 86 ++++++++++++++++++++--- srdl2sv/components/register.py | 10 ++- srdl2sv/components/templates/addrmap.yaml | 4 +- srdl2sv/components/templates/regs.yaml | 2 +- 6 files changed, 114 insertions(+), 22 deletions(-) diff --git a/srdl2sv/components/addrmap.py b/srdl2sv/components/addrmap.py index 40503e5..62b33a7 100644 --- a/srdl2sv/components/addrmap.py +++ b/srdl2sv/components/addrmap.py @@ -59,28 +59,49 @@ class AddrMap(Component): # Start assembling addrmap module self.logger.info("Starting to assemble input/output/inout ports") + # Prefetch dictionaries in local array + input_dict_list = [(key, value) for (key, value) in self.get_ports('input').items()] + output_dict_list = [(key, value) for (key, value) in self.get_ports('output').items()] + + input_signal_width = min( + max([len(value[0]) for (_, value) in input_dict_list]), 40) + + input_name_width = min( + max([len(key) for (key, _) in input_dict_list]), 40) + + output_signal_width = min( + max([len(value[0]) for (_, value) in output_dict_list]), 40) + + output_name_width = min( + max([len(key) for (key, _) in output_dict_list]), 40) + + # Input ports input_ports_rtl = [ AddrMap.templ_dict['input_port'].format( name = key, signal_type = value[0], + signal_width = input_signal_width, + name_width = input_name_width, unpacked_dim = '[{}]'.format( ']['.join( [str(y) for y in value[1]])) if value[1] else '') - for (key, value) in self.get_ports('input').items() + for (key, value) in input_dict_list ] # Output ports output_ports_rtl = [ AddrMap.templ_dict['output_port'].format( name = key, + signal_width = output_signal_width, + name_width = output_name_width, signal_type = value[0], unpacked_dim = '[{}]'.format( ']['.join( [str(y) for y in value[1]])) if value[1] else '') - for (key, value) in self.get_ports('output').items() + for (key, value) in output_dict_list ] # Remove comma from last port entry diff --git a/srdl2sv/components/component.py b/srdl2sv/components/component.py index f44eb03..6f245e1 100644 --- a/srdl2sv/components/component.py +++ b/srdl2sv/components/component.py @@ -7,19 +7,16 @@ from systemrdl import node from log.log import create_logger # Define NamedTuple -class TypeDefMembers(NamedTuple): - name: str - member_type: str - class TypeDef(NamedTuple): - name: str - members: list[TypeDefMembers] + scope: str + members: tuple class Component(): def __init__(self): self.rtl_header = [] self.rtl_footer = [] self.children = [] + self.typedef = dict() self.ports = dict() self.signals = dict() self.ports['input'] = dict() diff --git a/srdl2sv/components/field.py b/srdl2sv/components/field.py index 314646c..e716844 100644 --- a/srdl2sv/components/field.py +++ b/srdl2sv/components/field.py @@ -2,12 +2,12 @@ import math import importlib.resources as pkg_resources import yaml -from systemrdl import RDLCompiler, RDLCompileError, RDLWalker, RDLListener, node -from systemrdl.node import FieldNode +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 +from components.component import Component, TypeDef from . import templates class Field(Component): @@ -16,7 +16,7 @@ class Field(Component): pkg_resources.read_text(templates, 'fields.yaml'), Loader=yaml.FullLoader) - def __init__(self, obj: node.FieldNode, array_dimensions: list, config:dict): + def __init__(self, obj: FieldNode, array_dimensions: list, config:dict): super().__init__() # Save and/or process important variables @@ -26,6 +26,9 @@ class Field(Component): 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() + ################################################################################## # LIMITATION: # v1.x of the systemrdl-compiler does not support non-homogeneous arrays. @@ -67,7 +70,73 @@ class Field(Component): [self.yaml_signals_to_list(Field.templ_dict[i[1]]) for i in operations] - def __process_variables(self, obj: node.RootNode, array_dimensions: list): + def __process_fieldtype(self): + try: + 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): + if parent_scope.is_instance: + path.append(parent_scope.inst_name) + else: + path.append(parent_scope.type_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.typedef[enum_name] = TypeDef ( + scope=scope, + members= [(x.name, x.value) for x in self.obj.get_property('encode')] + ) + + # Save name of object + self.field_type =\ + '::'.join([scope, 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 + if self.obj.width > 1: + self.field_type = 'logic [{}:0]'.format(self.obj.width-1) + else: + self.field_type = 'logic' + + def __process_variables(self, obj: FieldNode, array_dimensions: list): # Save object self.obj = obj @@ -81,9 +150,6 @@ class Field(Component): self.path_underscored = self.path.replace('.', '_') self.path_wo_field = '.'.join(self.path.split('.', -1)[0:-1]) - # Field type - self.field_type = 'logic' - # Save dimensions of unpacked dimension self.array_dimensions = array_dimensions @@ -228,13 +294,13 @@ class Field(Component): swwe = self.obj.get_property('swwe') swwel = self.obj.get_property('swwel') - if isinstance(swwe, (node.FieldNode, node.SignalNode)): + if isinstance(swwe, (FieldNode, SignalNode)): access_rtl['sw_write'].append( Field.templ_dict['sw_access_field_swwe']['rtl'].format( path_wo_field = self.path_wo_field, genvars = self.genvars_str, swwe = Component.get_signal_name(swwe))) - elif isinstance(swwel, (node.FieldNode, node.SignalNode)): + elif isinstance(swwel, (FieldNode, SignalNode)): access_rtl['sw_write'].append( Field.templ_dict['sw_access_field_swwel']['rtl'].format( path_wo_field = self.path_wo_field, diff --git a/srdl2sv/components/register.py b/srdl2sv/components/register.py index 7805edf..98af9a5 100644 --- a/srdl2sv/components/register.py +++ b/srdl2sv/components/register.py @@ -70,16 +70,24 @@ class Register(Component): self.yaml_signals_to_list(Register.templ_dict[rw_wire_assign_field]) # Add wire/register instantiations + dict_list = [(key, value) for (key, value) in self.get_signals().items()] + + signal_width = min(max([len(value[0]) for (_, value) in dict_list]), 40) + + name_width = min(max([len(key) for (key, _) in dict_list]), 40) + self.rtl_header = [ *[ Register.templ_dict['signal_declaration'].format( name = key, type = value[0], + signal_width = signal_width, + name_width = name_width, unpacked_dim = '[{}]'.format( ']['.join( [str(y) for y in value[1]])) if value[1] else '') - for (key, value) in self.get_signals().items()], + for (key, value) in dict_list], '', *self.rtl_header, ] diff --git a/srdl2sv/components/templates/addrmap.yaml b/srdl2sv/components/templates/addrmap.yaml index a9a8610..e0d2391 100644 --- a/srdl2sv/components/templates/addrmap.yaml +++ b/srdl2sv/components/templates/addrmap.yaml @@ -11,6 +11,6 @@ module_declaration: |- {outputs} ); input_port: |- - input {signal_type:15s}{name:25s} {unpacked_dim}, + input {signal_type:{signal_width}} {name:{name_width}}{unpacked_dim}, output_port: |- - output {signal_type:15s}{name:25s} {unpacked_dim}, + output {signal_type:{signal_width}} {name:{name_width}}{unpacked_dim}, diff --git a/srdl2sv/components/templates/regs.yaml b/srdl2sv/components/templates/regs.yaml index 7650b6b..4c4b4dd 100644 --- a/srdl2sv/components/templates/regs.yaml +++ b/srdl2sv/components/templates/regs.yaml @@ -47,4 +47,4 @@ generate_for_start: |- generate_for_end: |- end // of for loop with iterator {dimension} signal_declaration: |- - {type:10s}{name:20s} {unpacked_dim}; + {type:{signal_width}} {name:{name_width}}{unpacked_dim};