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.
This commit is contained in:
Dennis Potter 2021-05-15 00:29:59 +02:00
parent 59b91536ed
commit cecb73f07a
Signed by: Dennis
GPG Key ID: 186A8AD440942BAF
11 changed files with 487 additions and 276 deletions

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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.")

View File

@ -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

View File

@ -1,9 +0,0 @@
module {addrmap_name} (
{bus_io}
{io_list}
);
{bus_widget}
{registers}
endmodule

View File

@ -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},

View File

@ -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

View File

@ -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}

View File

@ -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"""

View File

@ -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))