From cecb73f07a13cfed14b0b1c725288972259f19e6 Mon Sep 17 00:00:00 2001 From: Dennis Date: Sat, 15 May 2021 00:29:59 +0200 Subject: [PATCH] Fundamental changes to the architecture of component classes All components (e.g., fields, registers, addrmaps) are now children of a common class Component. This common class has certain common methods such as get_ports(), get_rtl(), or create_logger(). The AddrMap is now prepared to support alias registers by saving the registers in a dictionary. That way, registers can easily be accessed once an alias to a register is found. Furthermore, the addrmap template is now also loaded from a YAML file. Lastly, the first preparements to insert ports into the addrmap module are made. For templates, the indents do not need to be added anymore to the template. Now, a seperate method will automatically indent the RTL based on simple rules (e.g., increment indent if `begin` is found). The CLI also supports settings for the tabs (i.e., real tabs or spaces and the tab width). A lot of functionality from the __init__() method of the field class got reorganized. More logic will be reorganized in the future. --- srdl2sv/cli/cli.py | 57 +++- srdl2sv/components/addrmap.py | 107 +++++-- srdl2sv/components/component.py | 85 ++++++ srdl2sv/components/field.py | 336 ++++++++++++---------- srdl2sv/components/register.py | 76 +++-- srdl2sv/components/templates/addrmap.sv | 9 - srdl2sv/components/templates/addrmap.yaml | 21 ++ srdl2sv/components/templates/fields.yaml | 55 ++-- srdl2sv/components/templates/regs.yaml | 6 +- srdl2sv/log/log.py | 2 + srdl2sv/main.py | 9 +- 11 files changed, 487 insertions(+), 276 deletions(-) create mode 100644 srdl2sv/components/component.py create mode 100644 srdl2sv/components/templates/addrmap.yaml diff --git a/srdl2sv/cli/cli.py b/srdl2sv/cli/cli.py index 389f637..b6267fe 100644 --- a/srdl2sv/cli/cli.py +++ b/srdl2sv/cli/cli.py @@ -19,20 +19,6 @@ class CliArguments(): self.parser = argparse.ArgumentParser( description="SystemRDL 2 SystemVerilog compiler") - self.parser.add_argument( - "--stream_log_level", - choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL', 'NONE'], - default='WARNING', - help="Set verbosity level of output to shell. When set to 'NONE',\ - nothing will be printed to the shell. (default: %(default)s)") - - self.parser.add_argument( - "--file_log_level", - choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL', 'NONE'], - default='INFO', - help="Set verbosity level of output to log-file. When set to 'NONE',\ - nothing will be printed to the shell. (default: %(default)s)") - self.parser.add_argument( "-o", "--out_dir", @@ -55,7 +41,41 @@ class CliArguments(): "--recursive_search", action="store_true", help="If set, the dependency directories will be\ - searched recursively."); + searched recursively.") + + self.parser.add_argument( + "-x", + "--disable_sanity", + action="store_true", + help="Disable sanity checks or components. This might speed\ + up the compiler but is generally not recommended!") + + self.parser.add_argument( + "--stream_log_level", + choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL', 'NONE'], + default='WARNING', + help="Set verbosity level of output to shell. When set to 'NONE',\ + nothing will be printed to the shell. (default: %(default)s)") + + self.parser.add_argument( + "--file_log_level", + choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL', 'NONE'], + default='INFO', + help="Set verbosity level of output to log-file. When set to 'NONE',\ + nothing will be printed to the shell. (default: %(default)s)") + + self.parser.add_argument( + "--real_tabs", + action="store_true", + help="Use tabs, rather than spaces, for tabs") + + self.parser.add_argument( + "--tab_width", + type=int, + default=4, + help="Define how many tabs or spaces will be contained\ + in one level of indentation. (default: %(default)s)") + self.parser.add_argument( "IN_RDL", @@ -91,4 +111,11 @@ class CliArguments(): ts = time.strftime('%Y%m%d_%H%M%S', config['ts']) config['file_log_location'] = "srdl2sv_{}.log".format(ts) + # Tab style + config['real_tabs'] = args.real_tabs + config['tab_width'] = args.tab_width + + # Sanity check related + config['disable_sanity'] = args.disable_sanity + return config diff --git a/srdl2sv/components/addrmap.py b/srdl2sv/components/addrmap.py index 84fd5e7..11db616 100644 --- a/srdl2sv/components/addrmap.py +++ b/srdl2sv/components/addrmap.py @@ -1,56 +1,101 @@ -import yaml import re +import importlib.resources as pkg_resources +import yaml from systemrdl import RDLCompiler, RDLCompileError, RDLWalker, RDLListener, node from systemrdl.node import FieldNode # Local packages +from components.component import Component from components.register import Register from log.log import create_logger from . import templates -# Import templates -try: - import importlib.resources as pkg_resources -except ImportError: - # Try backported to PY<37 `importlib_resources`. - import importlib_resources as pkg_resources -class AddrMap: - def __init__(self, rdlc: RDLCompiler, obj: node.RootNode, config: dict): +class AddrMap(Component): + # Save YAML template as class variable + templ_dict = yaml.load( + pkg_resources.read_text(templates, 'addrmap.yaml'), + Loader=yaml.FullLoader) - self.rdlc = rdlc - self.name = obj.inst_name + def __init__(self, obj: node.RootNode, config: dict): + super().__init__() + + # Save and/or process important variables + self.__process_variables(obj) # Create logger object - self.logger = create_logger( - "{}.{}".format(__name__, obj.inst_name), - stream_log_level=config['stream_log_level'], - file_log_level=config['file_log_level'], - file_name=config['file_log_location']) - - self.logger.debug('Starting to process addrmap "{}"'.format(obj.inst_name)) + self.create_logger(self.path, config) + self.logger.debug('Starting to process addrmap') template = pkg_resources.read_text(templates, 'addrmap.sv') - # Read template for SystemVerilog module - tmpl_addrmap = re.compile("{addrmap_name}") - self.rtl = tmpl_addrmap.sub(obj.inst_name, template) - - # Empty list of register logic - self.registers = set() + # 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() # Traverse through children for child in obj.children(): if isinstance(child, node.AddrmapNode): - pass + self.logger.info('Found hierarchical addrmap. Entering it...') + self.logger.error('Child addrmaps are not implemented yet!') elif isinstance(child, node.RegfileNode): - pass + self.logger.error('Regfiles are not implemented yet!') elif isinstance(child, node.RegNode): - self.registers.add(Register(child, config)) + 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.registers[child.inst_name] = Register(child, config) - # TODO: Temporarily override RTL - self.rtl = [x.get_rtl() for x in self.registers] + # Add regfiles and registers to children + self.children = [x for x in self.registers.values()] - def get_rtl(self) -> str: - return '\n'.join(self.rtl) + self.logger.info("Done generating all child-regfiles/registers") + + # Start assembling addrmap module + self.logger.info("Starting to assemble input/output/inout ports") + + # Inout port + inout_ports_rtl = [ + AddrMap.templ_dict['inout_port'].format( + name = x) for x in self.get_ports('inout')] + # Input ports + input_ports_rtl = [ + AddrMap.templ_dict['input_port'].format( + name = x) for x in self.get_ports('input')] + # Output ports + output_ports_rtl = [ + AddrMap.templ_dict['output_port'].format( + name = x) for x in self.get_ports('output')] + + # Remove comma from last port entry + output_ports_rtl[-1] = output_ports_rtl[-1].rstrip(',') + + self.rtl_header.append( + AddrMap.templ_dict['module_declaration'].format( + name = obj.inst_name, + inouts = '\n'.join(inout_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 diff --git a/srdl2sv/components/component.py b/srdl2sv/components/component.py new file mode 100644 index 0000000..dfd2584 --- /dev/null +++ b/srdl2sv/components/component.py @@ -0,0 +1,85 @@ +import re +from itertools import chain + +# Local modules +from log.log import create_logger + +class Component(): + def __init__(self): + self.rtl_header = [] + self.rtl_footer = [] + self.children = [] + self.ports = dict() + self.ports['input'] = [] + self.ports['output'] = [] + self.ports['inout'] = [] + + def create_logger(self, name: str, config: dict): + self.logger = create_logger( + "{}".format(name), + stream_log_level=config['stream_log_level'], + file_log_level=config['file_log_level'], + file_name=config['file_log_location']) + + def get_ports(self, port_type: str): + self.logger.debug("Return port list") + return [ + *self.ports[port_type], + *list(chain(*[x.get_ports(port_type) for x in self.children])) + ] + + def get_rtl(self, tab_width: int = 0, real_tabs: bool = False) -> str: + self.logger.debug("Return RTL") + + # Loop through children and append RTL + rtl_children = [] + + for child in self.children: + rtl_children.append(child.get_rtl()) + + # Concatenate header, main, and footer + rtl = [*self.rtl_header, *rtl_children, *self.rtl_footer] + + # Join lists and return string + if tab_width > 0: + return Component.__add_tabs( + '\n'.join(rtl), + tab_width, + real_tabs) + + return '\n'.join(rtl) + + @staticmethod + def __add_tabs(rtl: str, tab_width: int = 4, real_tabs = False) -> str: + indent_lvl = 0 + + # Define tab style + tab = "\t" if real_tabs else " " + tab = tab_width * tab + + # 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') + + rtl_indented = [] + + # Go through RTL, line by line + 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 + + # 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 d861458..8381374 100644 --- a/srdl2sv/components/field.py +++ b/srdl2sv/components/field.py @@ -1,123 +1,61 @@ 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.rdltypes import PrecedenceType, AccessType -from itertools import chain # Local modules from log.log import create_logger +from components.component import Component +from . import templates -TAB = " " - -class Field: +class Field(Component): # Save YAML template as class variable - with open('srdl2sv/components/templates/fields.yaml', 'r') as file: - templ_dict = yaml.load(file, Loader=yaml.FullLoader) + templ_dict = yaml.load( + pkg_resources.read_text(templates, 'fields.yaml'), + Loader=yaml.FullLoader) - def __init__(self, obj: node.RootNode, indent_lvl: int, dimensions: int, config:dict): - self.obj = obj - self.rtl = [] - self.bytes = math.ceil(obj.width / 8) + def __init__(self, obj: node.RootNode, dimensions: int, config:dict): + super().__init__() + + # Save and/or process important variables + self.__process_variables(obj, dimensions) # Create logger object - self.logger = create_logger( - "{}.{}".format(__name__, obj.inst_name), - stream_log_level=config['stream_log_level'], - file_log_level=config['file_log_level'], - file_name=config['file_log_location']) - + self.create_logger("{}.{}".format(self.owning_addrmap, self.path), config) self.logger.debug('Starting to process field "{}"'.format(obj.inst_name)) - # Make a list of I/O that shall be added to the addrmap - self.input_ports = [] - self.output_ports = [] - ################################################################################## # 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 ################################################################################## - - # Determine resets. This includes checking for async/sync resets, - # the reset value, and whether the field actually has a reset - try: - rst_signal = obj.get_property("resetsignal") - rst_name = rst_signal.inst_name - rst_async = rst_signal.get_property("async") - rst_type = "asynchronous" if rst_async else "synchronous" - - # Active low or active high? - if rst_signal.get_property("activelow"): - rst_edge = "negedge" - rst_negl = "!" - rst_active = "active_low" - else: - rst_edge = "posedge" - rst_negl = "" - rst_active = "active_high" - - # Value of reset? - rst_value = '\'x' if obj.get_property("reset") == None else obj.get_property('reset') - except: - rst_async = False - rst_name = None - rst_negl = None - rst_edge = None - rst_value = "'x" - rst_active = "-" - rst_type = "-" - - # Get certain properties - hw_access = obj.get_property('hw') - sw_access = obj.get_property('sw') - precedence = obj.get_property('precedence') - - # Add comment with summary on field's properties - self.rtl.append( - Field.templ_dict['field_comment'].format( - name = obj.inst_name, - hw_access = str(hw_access)[11:], - sw_access = str(sw_access)[11:], - hw_precedence = '(precedence)' if precedence == PrecedenceType.hw else '', - sw_precedence = '(precedence)' if precedence == PrecedenceType.sw else '', - rst_active = rst_active, - rst_type = rst_type, - indent = self.indent(indent_lvl))) + # Print a summary + self.summary() # Handle always_ff - sense_list = 'sense_list_rst' if rst_async else 'sense_list_no_rst' + sense_list = 'sense_list_rst' if self.rst['async'] else 'sense_list_no_rst' - self.rtl.append( + self.rtl_header.append( Field.templ_dict[sense_list].format( clk_name = "clk", - rst_edge = rst_edge, - rst_name = rst_name, - indent = self.indent(indent_lvl))) - - - # Calculate how many genvars shall be added - genvars = ['[{}]'.format(chr(97+i)) for i in range(dimensions)] - genvars_str = ''.join(genvars) + rst_edge = self.rst['edge'], + rst_name = self.rst['name'])) # Add actual reset line - if rst_name: - indent_lvl += 1 - - self.rtl.append( + if self.rst['name']: + self.rtl_header.append( Field.templ_dict['rst_field_assign'].format( - field_name = obj.inst_name, - rst_name = rst_name, - rst_negl = rst_negl, - rst_value = rst_value, - genvars = genvars_str, - indent = self.indent(indent_lvl))) + path = self.path_underscored, + rst_name = self.rst['name'], + rst_negl = "!" if self.rst['active'] == "active_high" else "", + rst_value = self.rst['value'], + genvars = self.genvars_str)) - self.rtl.append("{}begin".format(self.indent(indent_lvl))) - - indent_lvl += 1 + self.rtl_header.append("begin") # Not all access types are required and the order might differ # depending on what types are defined and what precedence is @@ -133,67 +71,67 @@ class Field: # Define hardware access (if applicable) access_rtl['hw_write'] = [] - if hw_access in (AccessType.rw, AccessType.w): - if obj.get_property('we') or obj.get_property('wel'): + if self.hw_access in (AccessType.rw, AccessType.w): + if self.we_or_wel: access_rtl['hw_write'].append( Field.templ_dict['hw_access_we_wel'].format( negl = '!' if obj.get_property('wel') else '', - reg_name = obj.parent.inst_name, - field_name = obj.inst_name, - genvars = genvars_str, - indent = self.indent(indent_lvl))) + path = self.path_underscored, + genvars = self.genvars_str)) else: access_rtl['hw_write'].append( - Field.templ_dict['hw_access_no_we_wel'].format( - indent = self.indent(indent_lvl))) + Field.templ_dict['hw_access_no_we_wel']) access_rtl['hw_write'].append( Field.templ_dict['hw_access_field'].format( - reg_name = obj.parent.inst_name, - field_name = obj.inst_name, - genvars = genvars_str, - indent = self.indent(indent_lvl))) + path = self.path_underscored, + genvars = self.genvars_str)) # Define software access (if applicable) access_rtl['sw_write'] = [] - if sw_access in (AccessType.rw, AccessType.w): + if self.sw_access in (AccessType.rw, AccessType.w): access_rtl['sw_write'].append( Field.templ_dict['sw_access_field'].format( - reg_name = obj.parent.inst_name, - field_name = obj.inst_name, - genvars = genvars_str, - indent = self.indent(indent_lvl))) - - indent_lvl += 1 + path = self.path_underscored, + genvars = self.genvars_str)) # If field spans multiple bytes, every byte shall have a seperate enable! - for i in range(self.bytes): + for j, i in enumerate(range(self.lsbyte, self.msbyte+1)): access_rtl['sw_write'].append( Field.templ_dict['sw_access_byte'].format( - reg_name = obj.parent.inst_name, - field_name = obj.inst_name, - genvars = genvars_str, + path = self.path_underscored, + genvars = self.genvars_str, i = i, - indent = self.indent(indent_lvl))) + 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)))) - indent_lvl -= 1 + access_rtl['sw_write'].append("end") - access_rtl['sw_write'].append("{}end".format(self.indent(indent_lvl))) + # Add singlepulse property + access_rtl['singlepulse'] = [ + Field.templ_dict['singlepulse'].format( + path = self.path_underscored, + genvars = self.genvars_str) + ] - # Define else with correct indentation and add to dictionary - access_rtl['else'] = ["{}else".format(self.indent(indent_lvl))] + # Define else + access_rtl['else'] = ["else"] # Add empty string access_rtl[''] = [''] # Check if hardware has precedence (default `precedence = sw`) - if precedence == 'PrecedenceType.sw': + if self.precedence == 'PrecedenceType.sw': order_list = ['sw_write', - 'hw_write'] + 'hw_write', + 'singlepulse'] else: order_list = ['hw_write', - 'sw_write'] + 'sw_write', + 'singlepulse'] # Add appropriate else order_list_rtl = [] @@ -203,57 +141,149 @@ class Field: # get longer in the future and thus become unreadable if len(access_rtl[i]) > 0: order_list_rtl = [*order_list_rtl, *access_rtl[i]] - order_list_rtl.append("{}else".format(self.indent(indent_lvl))) + order_list_rtl.append("else") # Remove last pop order_list_rtl.pop() # Chain access RTL to the rest of the RTL - self.rtl = [*self.rtl, *order_list_rtl] + self.rtl_header = [*self.rtl_header, *order_list_rtl] - indent_lvl -= 1 - - self.rtl.append( + self.rtl_header.append( Field.templ_dict['end_field_ff'].format( - reg_name = obj.parent.inst_name, - field_name = obj.inst_name, - indent = self.indent(indent_lvl))) + path = self.path_underscored)) - ##################### - # Add combo logic - ##################### + + self.__add_combo() + self.__add_ports() + + def __add_combo(self): operations = [] - if obj.get_property('anded'): + if self.obj.get_property('anded'): operations.append(['anded', '&']) - if obj.get_property('ored'): + if self.obj.get_property('ored'): operations.append(['ored', '|']) - if obj.get_property('xored'): + if self.obj.get_property('xored'): operations.append(['xored', '^']) if len(operations) > 0: - self.rtl.append( + self.rtl_header.append( Field.templ_dict['combo_operation_comment'].format( - reg_name = obj.parent.inst_name, - field_name = obj.inst_name, - indent = self.indent(indent_lvl))) + path = self.path_underscored)) - self.rtl = [ - *self.rtl, + self.rtl_header = [ + *self.rtl_header, *[Field.templ_dict['assign_combo_operation'].format( - field_name = obj.inst_name, - reg_name = obj.parent.inst_name, - genvars = genvars_str, + path = self.path_underscored, + genvars = self.genvars_str, op_name = i[0], - op_verilog = i[1], - indent = self.indent(indent_lvl)) for i in operations]] - - # TODO: Set sanity checks. For example, having no we but precedence = hw - # will cause weird behavior. + op_verilog = i[1]) for i in operations] + ] - @staticmethod - def indent(level): - return TAB*level + def __process_variables(self, obj: node.RootNode, dimensions: int): + # Save object + self.obj = obj - def get_rtl(self) -> str: - return '\n'.join(self.rtl) + # 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.path_wo_field = '.'.join(self.path.split('.', -1)[0:-1]) + + # Calculate how many genvars shall be added + genvars = ['[{}]'.format(chr(97+i)) for i in range(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) + + # Determine resets. This includes checking for async/sync resets, + # the reset value, and whether the field actually has a reset + self.rst = dict() + + try: + rst_signal = obj.get_property("resetsignal") + + self.rst['name'] = rst_signal.inst_name + self.rst['async'] = rst_signal.get_property("async") + self.rst['type'] = "asynchronous" if rst['async'] else "synchronous" + + # Active low or active high? + if rst_signal.get_property("activelow"): + self.rst['edge'] = "negedge" + self.rst['active'] = "active_low" + else: + self.rst['edge'] = "posedge" + self.rst['active'] = "active_high" + + # Value of reset? + self.rst['value'] = '\'x' if obj.get_property("reset") == None else\ + obj.get_property('reset') + except: + self.rst['async'] = False + self.rst['name'] = None + self.rst['edge'] = None + self.rst['value'] = "'x" + self.rst['active'] = "-" + self.rst['type'] = "-" + + self.hw_access = obj.get_property('hw') + self.sw_access = obj.get_property('sw') + self.precedence = obj.get_property('precedence') + + + 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') + + # Add comment with summary on field's properties + self.rtl_header.append( + Field.templ_dict['field_comment'].format( + name = self.obj.inst_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 '', + rst_active = self.rst['active'], + rst_type = self.rst['type'], + misc_flags = misc_flags if misc_flags else '-', + lsb = self.obj.lsb, + msb = self.obj.msb, + path_wo_field = self.path_wo_field)) + + def __add_ports(self): + # Port is writable by hardware --> Input port from hardware + if self.hw_access in (AccessType.rw, AccessType.w): + self.ports['input'].append("{}_in".format(self.path_underscored)) + + # Port has enable signal --> create such an enable + if self.we_or_wel: + self.ports['input'].append("{}_hw_wr".format(self.path_underscored)) + + if self.hw_access in (AccessType.rw, AccessType.r): + self.ports['output'].append("{}_r".format(self.path_underscored)) + + 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.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.") diff --git a/srdl2sv/components/register.py b/srdl2sv/components/register.py index e7d6578..ff02d26 100644 --- a/srdl2sv/components/register.py +++ b/srdl2sv/components/register.py @@ -1,32 +1,29 @@ +import importlib.resources as pkg_resources import yaml from systemrdl import RDLCompiler, RDLCompileError, RDLWalker, RDLListener, node from systemrdl.node import FieldNode -from components.field import Field - # Local modules from log.log import create_logger +from components.component import Component +from components.field import Field +from . import templates -TAB = " " - -class Register: +class Register(Component): # Save YAML template as class variable - with open('srdl2sv/components/templates/regs.yaml', 'r') as file: - templ_dict = yaml.load(file, Loader=yaml.FullLoader) + templ_dict = yaml.load( + pkg_resources.read_text(templates, 'regs.yaml'), + Loader=yaml.FullLoader) def __init__(self, obj: node.RootNode, config: dict): - self.obj = obj - self.name = obj.inst_name - self.rtl = [] + super().__init__() + + # Save and/or process important variables + self.__process_variables(obj) # Create logger object - self.logger = create_logger( - "{}.{}".format(__name__, obj.inst_name), - stream_log_level=config['stream_log_level'], - file_log_level=config['file_log_level'], - file_name=config['file_log_location']) - + self.create_logger("{}.{}".format(self.owning_addrmap, self.path), config) self.logger.debug('Starting to process register "{}"'.format(obj.inst_name)) if obj.is_array: @@ -38,56 +35,55 @@ class Register: depth = '[{}]'.format(']['.join(f"{i}" for i in array_dimensions)) dimensions = len(array_dimensions) - indent_lvl = 0 # Create comment and provide user information about register he/she # is looking at. - self.rtl.append( + self.rtl_header.append( Register.templ_dict['reg_comment'].format( name = obj.inst_name, dimensions = dimensions, depth = depth)) # Create wires every register - self.rtl.append( + self.rtl_header.append( Register.templ_dict['rw_wire_declare'].format( name = obj.inst_name, depth = depth)) # Create generate block for register and add comment - self.rtl.append("generate") + self.rtl_header.append("generate") for i in range(dimensions): - self.rtl.append( + self.rtl_header.append( Register.templ_dict['generate_for_start'].format( iterator = chr(97+i), - limit = array_dimensions[i], - indent = self.indent(i))) - - indent_lvl = i - - indent_lvl += 1 + limit = array_dimensions[i])) # Create RTL for fields # Fields should be in order in RTL,therefore, use list - self.fields = [] - for field in obj.fields(): - field_obj = Field(field, indent_lvl, dimensions, config) - self.fields.append(field_obj) + field_obj = Field(field, dimensions, config) - self.rtl += field_obj.rtl + if not config['disable_sanity']: + field_obj.sanity_checks() + + self.children.append(field_obj) # End loops for i in range(dimensions-1, -1, -1): - self.rtl.append( + self.rtl_footer.append( Register.templ_dict['generate_for_end'].format( - dimension = chr(97+i), - indent = self.indent(i))) + dimension = chr(97+i))) + def __process_variables(self, obj: node.RootNode): + # Save object + self.obj = obj - @staticmethod - def indent(level): - return TAB*level + # Create full name + self.owning_addrmap = obj.owning_addrmap.inst_name + self.path = obj.get_path()\ + .replace('[]', '')\ + .replace('{}.'.format(self.owning_addrmap), '') - def get_rtl(self) -> str: - return '\n'.join(self.rtl) + self.path_underscored = self.path.replace('.', '_') + + self.name = obj.inst_name diff --git a/srdl2sv/components/templates/addrmap.sv b/srdl2sv/components/templates/addrmap.sv index 6702f69..e69de29 100644 --- a/srdl2sv/components/templates/addrmap.sv +++ b/srdl2sv/components/templates/addrmap.sv @@ -1,9 +0,0 @@ -module {addrmap_name} ( - {bus_io} - {io_list} -); - - {bus_widget} - - {registers} -endmodule diff --git a/srdl2sv/components/templates/addrmap.yaml b/srdl2sv/components/templates/addrmap.yaml new file mode 100644 index 0000000..24f3eb6 --- /dev/null +++ b/srdl2sv/components/templates/addrmap.yaml @@ -0,0 +1,21 @@ +--- +module_declaration: |- + module {name} ( + // Bus I/O + // TODO + + // InOuts + {inouts} + + // Inputs + {inputs} + + // Outputs + {outputs} + ); +input_port: |- + input {name}, +output_port: |- + output {name}, +inout_port: |- + inout {name}, diff --git a/srdl2sv/components/templates/fields.yaml b/srdl2sv/components/templates/fields.yaml index 82e1eb5..4a31f69 100644 --- a/srdl2sv/components/templates/fields.yaml +++ b/srdl2sv/components/templates/fields.yaml @@ -1,40 +1,49 @@ --- sense_list_rst: |- - {indent}always_ff @(posedge {clk_name} or {rst_edge} {rst_name}) + always_ff @(posedge {clk_name} or {rst_edge} {rst_name}) sense_list_no_rst: |- - {indent}always_ff @(posedge {clk_name}) + always_ff @(posedge {clk_name}) rst_field_assign: |- - {indent}if ({rst_negl}{rst_name}) - {indent} {field_name}_q{genvars} <= {rst_value}; - {indent}else + if ({rst_negl}{rst_name}) + begin + {path}_q{genvars} <= {rst_value}; + end + else sw_access_field: |- - {indent}if ({reg_name}_{field_name}_sw_wr{genvars}) - {indent}begin + if ({path}_sw_wr{genvars}) + begin sw_access_byte: |- - {indent}if (byte_enable[{i}]) - {indent} {reg_name}_{field_name}_q{genvars}[8*({i}+1)-1 -: 8] <= sw_wr_bus[8*({i}+1)-1 -: 8]; + if (byte_enable[{i}]) + begin + {path}_q{genvars}[{msb_field}-:{field_w}] <= sw_wr_bus[{msb_bus}-:{bus_w}]; + end hw_access_we_wel: |- - {indent}if ({negl}{reg_name}_{field_name}_hw_wr{genvars}) + if ({negl}{path}_hw_wr{genvars}) hw_access_no_we_wel: |- - {indent}if (1) // we or wel property not set + if (1) // we or wel property not set hw_access_field: |- - {indent}begin - {indent} {reg_name}_{field_name}_q{genvars} <= {reg_name}_{field_name}_in{genvars}; - {indent}end + begin + {path}_q{genvars} <= {path}_in{genvars}; + end end_field_ff: |- - {indent}end // of {reg_name}_{field_name}'s always_ff + end // of {path}'s always_ff field_comment: |- - {indent}//-----------------FIELD SUMMARY----------------- - {indent}// name : {name} - {indent}// access : hw = {hw_access} {hw_precedence} - {indent}// sw = {sw_access} {sw_precedence} - {indent}// reset : {rst_active} / {rst_type} - {indent}//----------------------------------------------- + //-----------------FIELD SUMMARY----------------- + // name : {name} ({path_wo_field}[{msb}:{lsb}]) + // access : hw = {hw_access} {hw_precedence} + // sw = {sw_access} {sw_precedence} + // reset : {rst_active} / {rst_type} + // flags : {misc_flags} + //----------------------------------------------- combo_operation_comment: |- - {indent}// Combinational logic for {reg_name}_{field_name} + // Combinational logic for {path} assign_combo_operation: |- - {indent}assign {reg_name}_{field_name}_{op_name}{genvars} = {op_verilog}{reg_name}_{field_name}_q{genvars} + assign {path}_{op_name}{genvars} = {op_verilog}{path}_q{genvars} +singlepulse: |- + begin + {path}{genvars} <= 0; + end diff --git a/srdl2sv/components/templates/regs.yaml b/srdl2sv/components/templates/regs.yaml index adf3801..533b099 100644 --- a/srdl2sv/components/templates/regs.yaml +++ b/srdl2sv/components/templates/regs.yaml @@ -14,7 +14,7 @@ reg_comment: |- ******************************************************************* *******************************************************************/ generate_for_start: |- - {indent}for ({iterator} = 0; {iterator} < {limit}; {iterator}++) - {indent}begin + for ({iterator} = 0; {iterator} < {limit}; {iterator}++) + begin generate_for_end: |- - {indent}end // of for loop with iterator {dimension} + end // of for loop with iterator {dimension} diff --git a/srdl2sv/log/log.py b/srdl2sv/log/log.py index 081497a..de97a8a 100644 --- a/srdl2sv/log/log.py +++ b/srdl2sv/log/log.py @@ -1,6 +1,8 @@ import logging from typing import Optional +# TODO: In general, it would be more useful if the hierarchy of the objects pop up +# in the logs, not the Python objectname and than the name of the SRDL object. class CustomFormatter(logging.Formatter): """Logging Formatter to add colors and count warning / errors""" diff --git a/srdl2sv/main.py b/srdl2sv/main.py index 58e6f76..b9a8552 100755 --- a/srdl2sv/main.py +++ b/srdl2sv/main.py @@ -40,7 +40,7 @@ if __name__ == "__main__": except RDLCompileError: sys.exit(1) - addrmap = AddrMap(rdlc, root.top, config) + addrmap = AddrMap(root.top, config) # Create output directory try: @@ -55,7 +55,12 @@ if __name__ == "__main__": out_file_name = "{}/{}.sv".format(config['output_dir'], addrmap.name) with open(out_file_name, 'w') as file: - file.write(addrmap.get_rtl()) + file.write( + addrmap.get_rtl( + tab_width=config['tab_width'], + real_tabs=config['real_tabs'] + ) + ) logger.info('Succesfully created "{}"'.format(out_file_name))