Dump addrmap packages with enums

This commit only adds package supports for addrmaps. It should be
relatively easy to extend this for regfiles in a future commit.

Furthermore, this commit adds support to _disable_ enums.
This commit is contained in:
Dennis Potter 2021-06-03 12:02:27 +02:00
parent fa7adf0a54
commit 4f6010eed2
Signed by: Dennis
GPG Key ID: 186A8AD440942BAF
8 changed files with 184 additions and 51 deletions

View File

@ -35,7 +35,7 @@ srdl2sv example_addrmap.rdl
## Help function ## Help function
A comprehensive help function of the tool can be invoked by running `srdl2sv --help`. 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] [--real_tabs] [--tab_width TAB_WIDTH]
IN_RDL [IN_RDL ...] IN_RDL [IN_RDL ...]
@ -53,6 +53,7 @@ optional arguments:
-r, --recursive_search -r, --recursive_search
If set, the dependency directories will be searched recursively. 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! -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} --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) 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} --file_log_level {DEBUG,INFO,WARNING,ERROR,CRITICAL,NONE}

View File

@ -50,6 +50,14 @@ class CliArguments():
help="Disable sanity checks or components. This might speed\ help="Disable sanity checks or components. This might speed\
up the compiler but is generally not recommended!") 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( self.parser.add_argument(
"--stream_log_level", "--stream_log_level",
choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL', 'NONE'], choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL', 'NONE'],
@ -118,4 +126,7 @@ class CliArguments():
# Sanity check related # Sanity check related
config['disable_sanity'] = args.disable_sanity config['disable_sanity'] = args.disable_sanity
# Set enums
config['enums'] = not args.disable_enums
return config return config

View File

@ -35,6 +35,7 @@ class AddrMap(Component):
# We need a dictionary since it might be required to access the objects later # We need a dictionary since it might be required to access the objects later
# by name (for example, in case of aliases) # by name (for example, in case of aliases)
self.registers = dict() self.registers = dict()
self.regfiles = []
# Traverse through children # Traverse through children
for child in obj.children(): for child in obj.children():
@ -44,7 +45,7 @@ class AddrMap(Component):
self.logger.info('Found hierarchical addrmap. Entering it...') self.logger.info('Found hierarchical addrmap. Entering it...')
self.logger.error('Child addrmaps are not implemented yet!') self.logger.error('Child addrmaps are not implemented yet!')
elif isinstance(child, node.RegfileNode): 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): elif isinstance(child, node.RegNode):
if child.inst.is_alias: if child.inst.is_alias:
# If the node we found is an alias, we shall not create a # 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 # Add registers to children. This must be done in a last step
# to account for all possible alias combinations # to account for all possible alias combinations
self.children = [ self.children = [
*self.children, *self.regfiles,
*[x for x in self.registers.values()] *[x for x in self.registers.values()]
] ]
@ -69,7 +70,7 @@ class AddrMap(Component):
# Reset ports # Reset ports
reset_ports_rtl = [ reset_ports_rtl = [
AddrMap.templ_dict['reset_port'].format( AddrMap.templ_dict['reset_port']['rtl'].format(
name = name) name = name)
for name in [x for x in self.get_resets()] for name in [x for x in self.get_resets()]
] ]
@ -93,7 +94,7 @@ class AddrMap(Component):
# Input ports # Input ports
input_ports_rtl = [ input_ports_rtl = [
AddrMap.templ_dict['input_port'].format( AddrMap.templ_dict['input_port']['rtl'].format(
name = key, name = key,
signal_type = value[0], signal_type = value[0],
signal_width = input_signal_width, signal_width = input_signal_width,
@ -107,7 +108,7 @@ class AddrMap(Component):
# Output ports # Output ports
output_ports_rtl = [ output_ports_rtl = [
AddrMap.templ_dict['output_port'].format( AddrMap.templ_dict['output_port']['rtl'].format(
name = key, name = key,
signal_width = output_signal_width, signal_width = output_signal_width,
name_width = output_name_width, name_width = output_name_width,
@ -122,13 +123,20 @@ class AddrMap(Component):
# Remove comma from last port entry # Remove comma from last port entry
output_ports_rtl[-1] = output_ports_rtl[-1].rstrip(',') 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( self.rtl_header.append(
AddrMap.templ_dict['module_declaration'].format( AddrMap.templ_dict['module_declaration']['rtl'].format(
name = self.name, name = self.name,
import_package_list = ',\n'.join(import_package_list),
resets = '\n'.join(reset_ports_rtl), resets = '\n'.join(reset_ports_rtl),
inputs = '\n'.join(input_ports_rtl), inputs = '\n'.join(input_ports_rtl),
outputs = '\n'.join(output_ports_rtl))) outputs = '\n'.join(output_ports_rtl)))
def __process_global_resets(self): def __process_global_resets(self):
field_reset_list = \ field_reset_list = \
[x for x in self.obj.signals() if x.get_property('field_reset')] [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) 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

View File

@ -9,6 +9,7 @@ from log.log import create_logger
# Define NamedTuple # Define NamedTuple
class TypeDef(NamedTuple): class TypeDef(NamedTuple):
scope: str scope: str
width: int
members: tuple members: tuple
class Component(): class Component():
@ -16,7 +17,7 @@ class Component():
self.rtl_header = [] self.rtl_header = []
self.rtl_footer = [] self.rtl_footer = []
self.children = [] self.children = []
self.typedef = dict() self.typedefs = dict()
self.ports = dict() self.ports = dict()
self.resets = set() self.resets = set()
self.signals = dict() self.signals = dict()
@ -33,6 +34,9 @@ class Component():
# Create path # Create path
self.create_underscored_path() self.create_underscored_path()
# Save config
self.config = config
# Create logger object # Create logger object
self.create_logger("{}.{}".format(self.owning_addrmap, self.path), config) self.create_logger("{}.{}".format(self.owning_addrmap, self.path), config)
self.logger.debug('Starting to process register "{}"'.format(obj.inst_name)) self.logger.debug('Starting to process register "{}"'.format(obj.inst_name))
@ -69,6 +73,14 @@ class Component():
return self.signals 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: def get_rtl(self, tab_width: int = 0, real_tabs: bool = False) -> str:
self.logger.debug("Return RTL") self.logger.debug("Return RTL")
@ -83,7 +95,7 @@ class Component():
# Join lists and return string # Join lists and return string
if tab_width > 0: if tab_width > 0:
return Component.__add_tabs( return Component.add_tabs(
'\n'.join(rtl), '\n'.join(rtl),
tab_width, tab_width,
real_tabs) real_tabs)
@ -91,8 +103,9 @@ class Component():
return '\n'.join(rtl) return '\n'.join(rtl)
@staticmethod @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 = 0
indent_lvl_next = 0
# Define tab style # Define tab style
tab = "\t" if real_tabs else " " tab = "\t" if real_tabs else " "
@ -100,8 +113,7 @@ class Component():
# Define triggers for which the indentation level will increment or # Define triggers for which the indentation level will increment or
# decrement on the next line # decrement on the next line
incr_trigger = re.compile('\\bbegin\\b') trigger_re = re.compile(r'.*?((?:\bbegin\b|\{)|(?:\bend\b|}))([^$]*)')
decr_trigger = re.compile('\\bend\\b')
rtl_indented = [] rtl_indented = []
@ -109,19 +121,33 @@ class Component():
for line in rtl.split('\n', -1): for line in rtl.split('\n', -1):
skip_incr_check = False skip_incr_check = False
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 # Check if indentation must be decremented
if decr_trigger.search(line): matchObj = trigger_re.match(line_split)
indent_lvl -= 1
skip_incr_check = True 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 # Add tabs
rtl_indented.append("{}{}".format(tab*indent_lvl, line)) 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) return '\n'.join(rtl_indented)

View File

@ -73,6 +73,9 @@ class Field(Component):
def __process_fieldtype(self): def __process_fieldtype(self):
try: try:
if not self.config['enums']:
raise AttributeError
enum = self.obj.get_property('encode') enum = self.obj.get_property('encode')
# Rules for scope: # Rules for scope:
@ -102,10 +105,7 @@ class Field(Component):
# Open up all parent scopes and append it to scope list # Open up all parent scopes and append it to scope list
while 1: while 1:
if isinstance(parent_scope, Regfile): if isinstance(parent_scope, Regfile):
if parent_scope.is_instance: path.append(parent_scope._scope_name)
path.append(parent_scope.inst_name)
else:
path.append(parent_scope.type_name)
# That's a lot of parent_scope's... # That's a lot of parent_scope's...
parent_scope = parent_scope.parent_scope parent_scope = parent_scope.parent_scope
@ -118,14 +118,15 @@ class Field(Component):
scope = '__'.join(reversed(path)) scope = '__'.join(reversed(path))
# Create internal NamedTuple with information on Enum # Create internal NamedTuple with information on Enum
self.typedef[enum_name] = TypeDef ( self.typedefs[enum_name] = TypeDef (
scope=scope, scope=scope,
width=self.obj.width,
members= [(x.name, x.value) for x in self.obj.get_property('encode')] members= [(x.name, x.value) for x in self.obj.get_property('encode')]
) )
# Save name of object # Save name of object
self.field_type =\ self.field_type =\
'::'.join([scope, enum_name]) '::'.join(['_'.join([scope, 'pkg']), enum_name])
self.logger.info("Parsed enum '{}'".format(enum_name)) self.logger.info("Parsed enum '{}'".format(enum_name))

View File

@ -55,6 +55,7 @@ class RegFile(Component):
# We need a dictionary since it might be required to access the objects later # We need a dictionary since it might be required to access the objects later
# by name (for example, in case of aliases) # by name (for example, in case of aliases)
self.registers = dict() self.registers = dict()
self.regfiles = []
# Set object to 0 for easy addressing # Set object to 0 for easy addressing
self.obj.current_idx = [0] self.obj.current_idx = [0]
@ -69,7 +70,7 @@ class RegFile(Component):
elif isinstance(child, node.RegfileNode): elif isinstance(child, node.RegfileNode):
self.obj.current_idx = [0] self.obj.current_idx = [0]
self.children.append( self.regfiles.append(
RegFile( RegFile(
child, child,
self.total_array_dimensions, self.total_array_dimensions,
@ -95,7 +96,7 @@ class RegFile(Component):
# Add registers to children. This must be done in a last step # Add registers to children. This must be done in a last step
# to account for all possible alias combinations # to account for all possible alias combinations
self.children = [ self.children = [
*self.children, *self.regfiles,
*[x for x in self.registers.values()] *[x for x in self.registers.values()]
] ]

View File

@ -1,6 +1,9 @@
--- ---
module_declaration: |- module_declaration:
module {name} ( rtl: |-
module {name}
{import_package_list};
(
// Clock & Resets // Clock & Resets
input reg_clk, input reg_clk,
input bus_clk, input bus_clk,
@ -15,9 +18,30 @@ module_declaration: |-
// Outputs // Outputs
{outputs} {outputs}
); );
reset_port: |- import_package:
rtl: |-
import {name}_pkg::*
reset_port:
rtl:
input {name}, input {name},
input_port: |- input_port:
rtl:
input {signal_type:{signal_width}} {name:{name_width}}{unpacked_dim}, input {signal_type:{signal_width}} {name:{name_width}}{unpacked_dim},
output_port: |- output_port:
rtl:
output {signal_type:{signal_width}} {name:{name_width}}{unpacked_dim}, 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}

View File

@ -52,9 +52,10 @@ if __name__ == "__main__":
config['output_dir'])) config['output_dir']))
# Save RTL to file # 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( file.write(
addrmap.get_rtl( addrmap.get_rtl(
tab_width=config['tab_width'], 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) logger.info("Elapsed time: %f seconds", time.time() - start)