diff --git a/README.md b/README.md index 6203224..0e61ae0 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ srdl2sv example_addrmap.rdl ## Help function A comprehensive help function of the tool can be invoked by running `srdl2sv --help`. ``` -usage: main.py [-h] [-o OUT_DIR] [-d SEARCH_PATHS [SEARCH_PATHS ...]] [-r] [-x] [--stream_log_level {DEBUG,INFO,WARNING,ERROR,CRITICAL,NONE}] [--file_log_level {DEBUG,INFO,WARNING,ERROR,CRITICAL,NONE}] +usage: main.py [-h] [-o OUT_DIR] [-d SEARCH_PATHS [SEARCH_PATHS ...]] [-r] [-x] [-e] [--stream_log_level {DEBUG,INFO,WARNING,ERROR,CRITICAL,NONE}] [--file_log_level {DEBUG,INFO,WARNING,ERROR,CRITICAL,NONE}] [--real_tabs] [--tab_width TAB_WIDTH] IN_RDL [IN_RDL ...] @@ -53,6 +53,7 @@ optional arguments: -r, --recursive_search If set, the dependency directories will be searched recursively. -x, --disable_sanity Disable sanity checks or components. This might speed up the compiler but is generally not recommended! + -e, --disable_enums Disable enumeration generation. This will prevent the compiler from generating packages and it will prevent it from using enums in the port list. --stream_log_level {DEBUG,INFO,WARNING,ERROR,CRITICAL,NONE} Set verbosity level of output to shell. When set to 'NONE', nothing will be printed to the shell. (default: WARNING) --file_log_level {DEBUG,INFO,WARNING,ERROR,CRITICAL,NONE} diff --git a/srdl2sv/cli/cli.py b/srdl2sv/cli/cli.py index b6267fe..e93c170 100644 --- a/srdl2sv/cli/cli.py +++ b/srdl2sv/cli/cli.py @@ -50,6 +50,14 @@ class CliArguments(): help="Disable sanity checks or components. This might speed\ up the compiler but is generally not recommended!") + self.parser.add_argument( + "-e", + "--disable_enums", + action="store_true", + help="Disable enumeration generation. This will prevent the\ + compiler from generating packages and it will prevent\ + it from using enums in the port list.") + self.parser.add_argument( "--stream_log_level", choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL', 'NONE'], @@ -118,4 +126,7 @@ class CliArguments(): # Sanity check related config['disable_sanity'] = args.disable_sanity + # Set enums + config['enums'] = not args.disable_enums + return config diff --git a/srdl2sv/components/addrmap.py b/srdl2sv/components/addrmap.py index 4c49860..d9cec90 100644 --- a/srdl2sv/components/addrmap.py +++ b/srdl2sv/components/addrmap.py @@ -35,6 +35,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 = [] # Traverse through children for child in obj.children(): @@ -44,7 +45,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.children.append(RegFile(child, [], [], config, glbl_settings)) + self.regfiles.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 @@ -58,7 +59,7 @@ class AddrMap(Component): # Add registers to children. This must be done in a last step # to account for all possible alias combinations self.children = [ - *self.children, + *self.regfiles, *[x for x in self.registers.values()] ] @@ -69,7 +70,7 @@ class AddrMap(Component): # Reset ports reset_ports_rtl = [ - AddrMap.templ_dict['reset_port'].format( + AddrMap.templ_dict['reset_port']['rtl'].format( name = name) for name in [x for x in self.get_resets()] ] @@ -93,7 +94,7 @@ class AddrMap(Component): # Input ports input_ports_rtl = [ - AddrMap.templ_dict['input_port'].format( + AddrMap.templ_dict['input_port']['rtl'].format( name = key, signal_type = value[0], signal_width = input_signal_width, @@ -107,7 +108,7 @@ class AddrMap(Component): # Output ports output_ports_rtl = [ - AddrMap.templ_dict['output_port'].format( + AddrMap.templ_dict['output_port']['rtl'].format( name = key, signal_width = output_signal_width, name_width = output_name_width, @@ -122,13 +123,20 @@ class AddrMap(Component): # Remove comma from last port entry output_ports_rtl[-1] = output_ports_rtl[-1].rstrip(',') + import_package_list = [] + [import_package_list.append( + AddrMap.templ_dict['import_package']['rtl'].format( + name = self.name)) for x in self.get_package_names()] + self.rtl_header.append( - AddrMap.templ_dict['module_declaration'].format( + AddrMap.templ_dict['module_declaration']['rtl'].format( name = self.name, + import_package_list = ',\n'.join(import_package_list), resets = '\n'.join(reset_ports_rtl), inputs = '\n'.join(input_ports_rtl), outputs = '\n'.join(output_ports_rtl))) + def __process_global_resets(self): field_reset_list = \ [x for x in self.obj.signals() if x.get_property('field_reset')] @@ -167,4 +175,55 @@ class AddrMap(Component): return (field_reset, cpuif_reset) + def get_package_names(self) -> set(): + names = set() + + for i in self.registers.values(): + for key, value in i.get_typedefs().items(): + names.add(value.scope) + + return names + + def get_package_rtl(self, tab_width: int = 4, real_tabs = False) -> dict(): + # First go through all registers in this scope to generate a package + package_rtl = [] + enum_rtl = [] + rtl_return = dict() + + for i in self.registers.values(): + for key, value in i.get_typedefs().items(): + variable_list = [] + + max_name_width = min( + max([len(x[0]) for x in value.members]), 40) + + for var in value.members: + variable_list.append( + AddrMap.templ_dict['enum_var_list_item']['rtl'].format( + value = var[1], + width = value.width, + max_name_width = max_name_width, + name = var[0])) + + enum_rtl.append( + AddrMap.templ_dict['enum_declaration']['rtl'].format( + width=value.width-1, + name = key, + enum_var_list = ',\n'.join(variable_list))) + + package_rtl =\ + AddrMap.templ_dict['package_declaration']['rtl'].format( + name = self.name, + pkg_content = '\n\n'.join(enum_rtl)) + + + rtl_return[self.name] = AddrMap.add_tabs( + package_rtl, + tab_width, + real_tabs) + + # TODO Later, request get_package_rtl()-method of all child regfiles + + return rtl_return + diff --git a/srdl2sv/components/component.py b/srdl2sv/components/component.py index 49f387c..3277c4e 100644 --- a/srdl2sv/components/component.py +++ b/srdl2sv/components/component.py @@ -9,6 +9,7 @@ from log.log import create_logger # Define NamedTuple class TypeDef(NamedTuple): scope: str + width: int members: tuple class Component(): @@ -16,7 +17,7 @@ class Component(): self.rtl_header = [] self.rtl_footer = [] self.children = [] - self.typedef = dict() + self.typedefs = dict() self.ports = dict() self.resets = set() self.signals = dict() @@ -33,6 +34,9 @@ class Component(): # Create path self.create_underscored_path() + # Save config + self.config = config + # Create logger object self.create_logger("{}.{}".format(self.owning_addrmap, self.path), config) self.logger.debug('Starting to process register "{}"'.format(obj.inst_name)) @@ -69,6 +73,14 @@ class Component(): return self.signals + def get_typedefs(self): + self.logger.debug("Return typedef list") + + for x in self.children: + self.typedefs |= x.get_typedefs() + + return self.typedefs + def get_rtl(self, tab_width: int = 0, real_tabs: bool = False) -> str: self.logger.debug("Return RTL") @@ -83,7 +95,7 @@ class Component(): # Join lists and return string if tab_width > 0: - return Component.__add_tabs( + return Component.add_tabs( '\n'.join(rtl), tab_width, real_tabs) @@ -91,8 +103,9 @@ class Component(): return '\n'.join(rtl) @staticmethod - def __add_tabs(rtl: str, tab_width: int = 4, real_tabs = False) -> str: + def add_tabs(rtl: str, tab_width: int = 4, real_tabs = False) -> str: indent_lvl = 0 + indent_lvl_next = 0 # Define tab style tab = "\t" if real_tabs else " " @@ -100,8 +113,7 @@ class Component(): # Define triggers for which the indentation level will increment or # decrement on the next line - incr_trigger = re.compile('\\bbegin\\b') - decr_trigger = re.compile('\\bend\\b') + trigger_re = re.compile(r'.*?((?:\bbegin\b|\{)|(?:\bend\b|}))([^$]*)') rtl_indented = [] @@ -109,19 +121,33 @@ class Component(): for line in rtl.split('\n', -1): skip_incr_check = False - # Check if indentation must be decremented - if decr_trigger.search(line): - indent_lvl -= 1 - skip_incr_check = True + line_split = line + + # This is done because the increment of the indent level must + # be delayed one cycle + indent_lvl = indent_lvl_next + + while 1: + # Check if indentation must be decremented + matchObj = trigger_re.match(line_split) + + if matchObj: + if matchObj.group(1) in ('begin', '{'): + indent_lvl_next += 1 + else: + indent_lvl = indent_lvl_next - 1 + indent_lvl_next -= 1 + + line_split = matchObj.group(2) + + if not line_split: + break + else: + break # Add tabs rtl_indented.append("{}{}".format(tab*indent_lvl, line)) - # Check if tab level must be incremented - if skip_incr_check: - continue - elif incr_trigger.search(line): - indent_lvl += 1 return '\n'.join(rtl_indented) diff --git a/srdl2sv/components/field.py b/srdl2sv/components/field.py index 83634fc..f44b7bf 100644 --- a/srdl2sv/components/field.py +++ b/srdl2sv/components/field.py @@ -73,6 +73,9 @@ class Field(Component): def __process_fieldtype(self): try: + if not self.config['enums']: + raise AttributeError + enum = self.obj.get_property('encode') # Rules for scope: @@ -102,10 +105,7 @@ class Field(Component): # 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) + path.append(parent_scope._scope_name) # That's a lot of parent_scope's... parent_scope = parent_scope.parent_scope @@ -118,14 +118,15 @@ class Field(Component): scope = '__'.join(reversed(path)) # Create internal NamedTuple with information on Enum - self.typedef[enum_name] = TypeDef ( + 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([scope, enum_name]) + '::'.join(['_'.join([scope, 'pkg']), enum_name]) self.logger.info("Parsed enum '{}'".format(enum_name)) diff --git a/srdl2sv/components/regfile.py b/srdl2sv/components/regfile.py index aa686e7..991d58a 100644 --- a/srdl2sv/components/regfile.py +++ b/srdl2sv/components/regfile.py @@ -55,6 +55,7 @@ class RegFile(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 = [] # Set object to 0 for easy addressing self.obj.current_idx = [0] @@ -69,7 +70,7 @@ class RegFile(Component): elif isinstance(child, node.RegfileNode): self.obj.current_idx = [0] - self.children.append( + self.regfiles.append( RegFile( child, self.total_array_dimensions, @@ -95,7 +96,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.children, + *self.regfiles, *[x for x in self.registers.values()] ] diff --git a/srdl2sv/components/templates/addrmap.yaml b/srdl2sv/components/templates/addrmap.yaml index dd4919d..afda847 100644 --- a/srdl2sv/components/templates/addrmap.yaml +++ b/srdl2sv/components/templates/addrmap.yaml @@ -1,23 +1,47 @@ --- -module_declaration: |- - module {name} ( - // Clock & Resets - input reg_clk, - input bus_clk, - {resets} +module_declaration: + rtl: |- + module {name} + {import_package_list}; + ( + // Clock & Resets + input reg_clk, + input bus_clk, + {resets} - // Bus I/O - // TODO + // Bus I/O + // TODO - // Inputs - {inputs} + // Inputs + {inputs} - // Outputs - {outputs} - ); -reset_port: |- - input {name}, -input_port: |- - input {signal_type:{signal_width}} {name:{name_width}}{unpacked_dim}, -output_port: |- - output {signal_type:{signal_width}} {name:{name_width}}{unpacked_dim}, + // Outputs + {outputs} + ); +import_package: + rtl: |- + import {name}_pkg::* +reset_port: + rtl: + input {name}, +input_port: + rtl: + input {signal_type:{signal_width}} {name:{name_width}}{unpacked_dim}, +output_port: + rtl: + output {signal_type:{signal_width}} {name:{name_width}}{unpacked_dim}, +package_declaration: + rtl: |- + package {name}_pkg; + + {pkg_content} + + endpackage +enum_declaration: + rtl: |- + typedef enum logic [{width}:0] {{ + {enum_var_list} + }} {name}; +enum_var_list_item: + rtl: |- + {name:{max_name_width}} = {width}'d{value} diff --git a/srdl2sv/main.py b/srdl2sv/main.py index b9a8552..bfdb633 100755 --- a/srdl2sv/main.py +++ b/srdl2sv/main.py @@ -52,9 +52,10 @@ if __name__ == "__main__": config['output_dir'])) # Save RTL to file - out_file_name = "{}/{}.sv".format(config['output_dir'], addrmap.name) + # Start out with addrmap + out_addrmap_file = "{}/{}.sv".format(config['output_dir'], addrmap.name) - with open(out_file_name, 'w') as file: + with open(out_addrmap_file, 'w') as file: file.write( addrmap.get_rtl( tab_width=config['tab_width'], @@ -62,6 +63,15 @@ if __name__ == "__main__": ) ) - logger.info('Succesfully created "{}"'.format(out_file_name)) + logger.info('Succesfully created "{}"'.format(out_addrmap_file)) + + # Start grabbing packages. This returns a dictionary for the main addrmap + # and all it's child regfiles/addrmaps + for key, value in addrmap.get_package_rtl( + tab_width=config['tab_width'], + real_tabs=config['real_tabs'] + ).items(): + with open('{}/{}_pkg.sv'.format(config['output_dir'], key), 'w') as file: + file.write(value) logger.info("Elapsed time: %f seconds", time.time() - start)