Add regfile capabilities

Apart from adding regfiles in general (which is mostly a combination of
addrmap and register code), the stride/array_dimension code had to be
revisted to be correct for multi dimensional arrays with multi
dimensional registers.

Lastly, the logger instantiation has been moved to the __init__()
method of `Component`.
This commit is contained in:
Dennis Potter 2021-05-31 00:37:41 +02:00
parent 2a3cc9505e
commit 8a82d37737
Signed by: Dennis
GPG Key ID: 186A8AD440942BAF
9 changed files with 276 additions and 81 deletions

View File

@ -2,11 +2,12 @@ import re
import importlib.resources as pkg_resources import importlib.resources as pkg_resources
import yaml import yaml
from systemrdl import RDLCompiler, RDLCompileError, RDLWalker, RDLListener, node from systemrdl import node
from systemrdl.node import FieldNode from systemrdl.node import FieldNode
# Local packages # Local packages
from components.component import Component from components.component import Component
from components.regfile import RegFile
from components.register import Register from components.register import Register
from . import templates from . import templates
@ -18,17 +19,12 @@ class AddrMap(Component):
Loader=yaml.FullLoader) Loader=yaml.FullLoader)
def __init__(self, obj: node.RootNode, config: dict): def __init__(self, obj: node.RootNode, config: dict):
super().__init__() super().__init__(obj, config)
# Save and/or process important variables
self.__process_variables(obj)
# Create logger object # Create logger object
self.create_logger(self.path, config) self.create_logger(self.path, config)
self.logger.debug('Starting to process addrmap') self.logger.debug('Starting to process addrmap')
template = pkg_resources.read_text(templates, 'addrmap.sv')
# Check if global resets are defined # Check if global resets are defined
glbl_settings = dict() glbl_settings = dict()
@ -48,7 +44,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.logger.error('Regfiles are not implemented yet!') self.children.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
@ -57,15 +53,19 @@ class AddrMap(Component):
self.logger.error('Alias registers are not implemented yet!') self.logger.error('Alias registers are not implemented yet!')
else: else:
self.registers[child.inst_name] = \ self.registers[child.inst_name] = \
Register(child, config, glbl_settings) Register(child, [], [], config, glbl_settings)
# Add regfiles and registers to children # Add registers to children. This must be done in a last step
self.children = [x for x in self.registers.values()] # to account for all possible alias combinations
self.children = [
*self.children,
*[x for x in self.registers.values()]
]
self.logger.info("Done generating all child-regfiles/registers") self.logger.info("Done generating all child-regfiles/registers")
# Start assembling addrmap module # Start assembling addrmap module
self.logger.info("Starting to assemble input/output/inout ports") self.logger.info("Starting to assemble input & output ports")
# Reset ports # Reset ports
reset_ports_rtl = [ reset_ports_rtl = [
@ -124,25 +124,11 @@ class AddrMap(Component):
self.rtl_header.append( self.rtl_header.append(
AddrMap.templ_dict['module_declaration'].format( AddrMap.templ_dict['module_declaration'].format(
name = obj.inst_name, name = self.name,
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_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
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')]

View File

@ -12,7 +12,7 @@ class TypeDef(NamedTuple):
members: tuple members: tuple
class Component(): class Component():
def __init__(self): def __init__(self, obj, config):
self.rtl_header = [] self.rtl_header = []
self.rtl_footer = [] self.rtl_footer = []
self.children = [] self.children = []
@ -24,6 +24,19 @@ class Component():
self.ports['output'] = dict() self.ports['output'] = dict()
self.field_type = '' self.field_type = ''
# Save object
self.obj = obj
# Save name
self.name = obj.inst_name
# Create path
self.create_underscored_path()
# Create logger object
self.create_logger("{}.{}".format(self.owning_addrmap, self.path), config)
self.logger.debug('Starting to process register "{}"'.format(obj.inst_name))
def create_logger(self, name: str, config: dict): def create_logger(self, name: str, config: dict):
self.logger = create_logger( self.logger = create_logger(
"{}".format(name), "{}".format(name),
@ -121,7 +134,7 @@ class Component():
@staticmethod @staticmethod
def split_dimensions(path: str): def split_dimensions(path: str):
re_dimensions = re.compile('(\[[^]]\])') re_dimensions = re.compile('(\[[^]]*\])')
new_path = re_dimensions.sub('', path) new_path = re_dimensions.sub('', path)
return (new_path, ''.join(re_dimensions.findall(path))) return (new_path, ''.join(re_dimensions.findall(path)))
@ -159,7 +172,7 @@ class Component():
for x in yaml_obj['signals']: for x in yaml_obj['signals']:
self.signals[x['name'].format(path = self.path_underscored)] =\ self.signals[x['name'].format(path = self.path_underscored)] =\
(x['signal_type'].format(field_type = self.field_type), (x['signal_type'].format(field_type = self.field_type),
self.array_dimensions) self.total_array_dimensions)
except (TypeError, KeyError): except (TypeError, KeyError):
pass pass
@ -167,7 +180,7 @@ class Component():
for x in yaml_obj['input_ports']: for x in yaml_obj['input_ports']:
self.ports['input'][x['name'].format(path = self.path_underscored)] =\ self.ports['input'][x['name'].format(path = self.path_underscored)] =\
(x['signal_type'].format(field_type = self.field_type), (x['signal_type'].format(field_type = self.field_type),
self.array_dimensions) self.total_array_dimensions)
except (TypeError, KeyError): except (TypeError, KeyError):
pass pass
@ -175,7 +188,7 @@ class Component():
for x in yaml_obj['output_ports']: for x in yaml_obj['output_ports']:
self.ports['output'][x['name'].format(path = self.path_underscored)] =\ self.ports['output'][x['name'].format(path = self.path_underscored)] =\
(x['signal_type'].format(field_type = self.field_type), (x['signal_type'].format(field_type = self.field_type),
self.array_dimensions) self.total_array_dimensions)
except (TypeError, KeyError): except (TypeError, KeyError):
pass pass
@ -204,3 +217,11 @@ class Component():
rst['type'] = "-" rst['type'] = "-"
return rst return rst
def create_underscored_path(self):
self.owning_addrmap = self.obj.owning_addrmap.inst_name
self.full_path = Component.split_dimensions(self.obj.get_path())[0]
self.path = self.full_path\
.replace('{}.'.format(self.owning_addrmap), '')
self.path_underscored = self.path.replace('.', '__')

View File

@ -16,16 +16,17 @@ class Field(Component):
pkg_resources.read_text(templates, 'fields.yaml'), pkg_resources.read_text(templates, 'fields.yaml'),
Loader=yaml.FullLoader) Loader=yaml.FullLoader)
def __init__(self, obj: FieldNode, array_dimensions: list, config:dict, glbl_settings: dict): def __init__(
super().__init__() self,
obj: FieldNode,
array_dimensions: list,
config:dict,
glbl_settings: dict):
super().__init__(obj, config)
# Save and/or process important variables # Save and/or process important variables
self.__process_variables(obj, array_dimensions, glbl_settings) self.__process_variables(obj, array_dimensions, glbl_settings)
# Create logger object
self.create_logger("{}.{}".format(self.owning_addrmap, self.path), config)
self.logger.debug('Starting to process field "{}"'.format(obj.inst_name))
# Determine field types # Determine field types
self.__process_fieldtype() self.__process_fieldtype()
@ -137,21 +138,12 @@ class Field(Component):
self.field_type = 'logic' self.field_type = 'logic'
def __process_variables(self, obj: FieldNode, array_dimensions: list, glbl_settings: dict): def __process_variables(self, obj: FieldNode, array_dimensions: list, glbl_settings: dict):
# Save object
self.obj = obj
# Create full name # Create full name
self.owning_addrmap = obj.owning_addrmap.inst_name
self.full_path = obj.get_path().replace('[]', '')
self.path = self.full_path\
.replace('[]', '')\
.replace('{}.'.format(self.owning_addrmap), '')
self.path_underscored = self.path.replace('.', '_')
self.path_wo_field = '.'.join(self.path.split('.', -1)[0:-1]) self.path_wo_field = '.'.join(self.path.split('.', -1)[0:-1])
# Save dimensions of unpacked dimension # Save dimensions of unpacked dimension
self.array_dimensions = array_dimensions self.array_dimensions = array_dimensions
self.total_array_dimensions = array_dimensions
# Calculate how many genvars shall be added # Calculate how many genvars shall be added
genvars = ['[{}]'.format(chr(97+i)) for i in range(len(array_dimensions))] genvars = ['[{}]'.format(chr(97+i)) for i in range(len(array_dimensions))]
@ -170,7 +162,7 @@ class Field(Component):
reset_signal = obj.get_property("resetsignal") reset_signal = obj.get_property("resetsignal")
if (reset_signal): if reset_signal:
self.rst = Field.process_reset_signal(reset_signal) self.rst = Field.process_reset_signal(reset_signal)
else: else:
# Only use global reset (if present) if no local reset is set # Only use global reset (if present) if no local reset is set
@ -180,7 +172,7 @@ class Field(Component):
# Value of reset must always be determined on field level # Value of reset must always be determined on field level
self.rst['value'] = \ self.rst['value'] = \
'\'x' if obj.get_property("reset") == None else\ '\'x' if not obj.get_property("reset") else\
obj.get_property('reset') obj.get_property('reset')
# Define hardware access # Define hardware access
@ -201,7 +193,7 @@ class Field(Component):
# Add comment with summary on field's properties # Add comment with summary on field's properties
return \ return \
Field.templ_dict['field_comment']['rtl'].format( Field.templ_dict['field_comment']['rtl'].format(
name = self.obj.inst_name, name = self.name,
hw_access = str(self.hw_access)[11:], hw_access = str(self.hw_access)[11:],
sw_access = str(self.sw_access)[11:], sw_access = str(self.sw_access)[11:],
hw_precedence = '(precedence)' if self.precedence == PrecedenceType.hw else '', hw_precedence = '(precedence)' if self.precedence == PrecedenceType.hw else '',

View File

@ -0,0 +1,142 @@
import re
import importlib.resources as pkg_resources
import sys
import math
import yaml
from systemrdl import node
from systemrdl.node import FieldNode
# Local packages
from components.component import Component
from components.register import Register
from . import templates
class RegFile(Component):
# Save YAML template as class variable
templ_dict = yaml.load(
pkg_resources.read_text(templates, 'regfile.yaml'),
Loader=yaml.FullLoader)
def __init__(
self,
obj: node.RegfileNode,
parents_dimensions: list,
parents_stride: list,
config: dict,
glbl_settings: dict):
super().__init__(obj, config)
# Save and/or process important variables
self.__process_variables(obj, parents_dimensions, parents_stride)
# Create comment and provide user information about register he/she
# is looking at.
self.rtl_header = [
RegFile.templ_dict['regfile_comment']['rtl'].format(
name = obj.inst_name,
dimensions = self.dimensions,
depth = self.depth),
*self.rtl_header
]
# Create generate block for register and add comment
if self.dimensions:
self.rtl_header.append("generate")
for i in range(self.dimensions):
self.rtl_header.append(
RegFile.templ_dict['generate_for_start']['rtl'].format(
iterator = chr(97+i+self.parents_depths),
limit = self.array_dimensions[i]))
# 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()
# Set object to 0 for easy addressing
self.obj.current_idx = [0]
# Traverse through children
for child in obj.children():
if isinstance(child, node.AddrmapNode):
self.logger.fatal('Instantiating addrmaps within regfiles is not '\
'supported. Addrmaps shall be instantiated at the '\
'top-level of other addrmaps')
sys.exit(1)
elif isinstance(child, node.RegfileNode):
self.obj.current_idx = [0]
self.children.append(
RegFile(
child,
self.total_array_dimensions,
self.total_stride,
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
# new register. Rather, we bury up the old register and add
# additional properties
self.logger.error('Alias registers are not implemented yet!')
else:
self.obj.current_idx = [0]
self.registers[child.inst_name] = \
Register(
child,
self.total_array_dimensions,
self.total_stride,
config,
glbl_settings)
# Add registers to children. This must be done in a last step
# to account for all possible alias combinations
self.children = [
*self.children,
*[x for x in self.registers.values()]
]
self.logger.info("Done generating all child-regfiles/registers")
def __process_variables(self,
obj: node.RegfileNode,
parents_dimensions: list,
parents_stride: list):
# Determine dimensions of register
if obj.is_array:
self.sel_arr = 'array'
self.total_array_dimensions = [*parents_dimensions, *self.obj.array_dimensions]
self.array_dimensions = self.obj.array_dimensions
# Merge parent's stride with stride of this regfile. Before doing so, the
# respective stride of the different dimensions shall be calculated
self.total_stride = [
*parents_stride,
*[math.prod(self.array_dimensions[i+1:])
*self.obj.array_stride
for i, _ in enumerate(self.array_dimensions)]
]
else:
self.sel_arr = 'single'
self.total_array_dimensions = parents_dimensions
self.array_dimensions = []
self.total_stride = parents_stride
# How many dimensions were already part of some higher up hierarchy?
self.parents_depths = len(parents_dimensions)
self.total_depth = '[{}]'.format(']['.join(f"{i}" for i in self.total_array_dimensions))
self.total_dimensions = len(self.total_array_dimensions)
self.depth = '[{}]'.format(']['.join(f"{i}" for i in self.array_dimensions))
self.dimensions = len(self.array_dimensions)
# Calculate how many genvars shall be added
genvars = ['[{}]'.format(chr(97+i)) for i in range(self.dimensions)]
self.genvars_str = ''.join(genvars)

View File

@ -1,5 +1,6 @@
import importlib.resources as pkg_resources import importlib.resources as pkg_resources
import yaml import yaml
import math
from systemrdl import node from systemrdl import node
@ -14,20 +15,22 @@ class Register(Component):
pkg_resources.read_text(templates, 'regs.yaml'), pkg_resources.read_text(templates, 'regs.yaml'),
Loader=yaml.FullLoader) Loader=yaml.FullLoader)
def __init__(self, obj: node.RootNode, config: dict, glbl_settings: dict): def __init__(
super().__init__() self,
obj: node.RegNode,
parents_dimensions: list,
parents_stride: list,
config: dict,
glbl_settings: dict):
super().__init__(obj, config)
# Save and/or process important variables # Save and/or process important variables
self.__process_variables(obj) self.__process_variables(obj, parents_dimensions, parents_stride)
# Create logger object
self.create_logger("{}.{}".format(self.owning_addrmap, self.path), config)
self.logger.debug('Starting to process register "{}"'.format(obj.inst_name))
# Create RTL for fields # Create RTL for fields
# Fields should be in order in RTL,therefore, use list # Fields should be in order in RTL,therefore, use list
for field in obj.fields(): for field in obj.fields():
field_obj = Field(field, self.array_dimensions, config, glbl_settings) field_obj = Field(field, self.total_array_dimensions, config, glbl_settings)
if not config['disable_sanity']: if not config['disable_sanity']:
field_obj.sanity_checks() field_obj.sanity_checks()
@ -35,11 +38,13 @@ class Register(Component):
self.children.append(field_obj) self.children.append(field_obj)
# Create generate block for register and add comment # Create generate block for register and add comment
if self.dimensions:
self.rtl_header.append("generate") self.rtl_header.append("generate")
for i in range(self.dimensions): for i in range(self.dimensions):
self.rtl_header.append( self.rtl_header.append(
Register.templ_dict['generate_for_start'].format( Register.templ_dict['generate_for_start'].format(
iterator = chr(97+i), iterator = chr(97+i+self.parents_depths),
limit = self.array_dimensions[i])) limit = self.array_dimensions[i]))
@ -49,6 +54,9 @@ class Register(Component):
Register.templ_dict['generate_for_end'].format( Register.templ_dict['generate_for_end'].format(
dimension = chr(97+i))) dimension = chr(97+i)))
if self.dimensions:
self.rtl_footer.append("endgenerate")
# Assign variables from bus # Assign variables from bus
self.obj.current_idx = [0] self.obj.current_idx = [0]
@ -59,11 +67,10 @@ class Register(Component):
self.rtl_header.append( self.rtl_header.append(
Register.templ_dict[rw_wire_assign_field]['rtl'].format( Register.templ_dict[rw_wire_assign_field]['rtl'].format(
path = self.path, path = self.path_underscored,
addr = self.obj.absolute_address, addr = self.obj.absolute_address,
genvars = self.genvars_str, genvars = self.genvars_str,
genvars_sum =self.genvars_sum_str, genvars_sum =self.genvars_sum_str,
stride = self.obj.array_stride,
depth = self.depth)) depth = self.depth))
self.yaml_signals_to_list(Register.templ_dict[rw_wire_assign_field]) self.yaml_signals_to_list(Register.templ_dict[rw_wire_assign_field])
@ -102,7 +109,11 @@ class Register(Component):
] ]
def __process_variables(self, obj: node.RootNode): def __process_variables(
self,
obj: node.RegNode,
parents_dimensions: list,
parents_stride: list):
# Save object # Save object
self.obj = obj self.obj = obj
@ -110,37 +121,62 @@ class Register(Component):
self.name = obj.inst_name self.name = obj.inst_name
# Create full name # Create full name
self.owning_addrmap = obj.owning_addrmap.inst_name self.create_underscored_path()
self.path = obj.get_path()\
.replace('[]', '')\
.replace('{}.'.format(self.owning_addrmap), '')
self.path_underscored = self.path.replace('.', '_')
# Determine dimensions of register # Determine dimensions of register
if obj.is_array: if obj.is_array:
self.sel_arr = 'array' self.sel_arr = 'array'
self.total_array_dimensions = [*parents_dimensions, *self.obj.array_dimensions]
self.array_dimensions = self.obj.array_dimensions self.array_dimensions = self.obj.array_dimensions
# Merge parent's stride with stride of this regfile. Before doing so, the
# respective stride of the different dimensions shall be calculated
self.total_stride = [
*parents_stride,
*[math.prod(self.array_dimensions[i+1:])
*self.obj.array_stride
for i, _ in enumerate(self.array_dimensions)]
]
else: else:
self.sel_arr = 'single' self.sel_arr = 'single'
self.total_array_dimensions = parents_dimensions
self.array_dimensions = [] self.array_dimensions = []
self.total_stride = self.obj.array_stride
# How many dimensions were already part of some higher up hierarchy?
self.parents_depths = len(parents_dimensions)
self.total_depth = '[{}]'.format(']['.join(f"{i}" for i in self.total_array_dimensions))
self.total_dimensions = len(self.total_array_dimensions)
self.depth = '[{}]'.format(']['.join(f"{i}" for i in self.array_dimensions)) self.depth = '[{}]'.format(']['.join(f"{i}" for i in self.array_dimensions))
self.dimensions = len(self.array_dimensions) self.dimensions = len(self.array_dimensions)
# Calculate how many genvars shall be added # Calculate how many genvars shall be added
genvars = ['[{}]'.format(chr(97+i)) for i in range(self.dimensions)] genvars = ['[{}]'.format(chr(97+i)) for i in range(self.total_dimensions)]
self.genvars_str = ''.join(genvars) self.genvars_str = ''.join(genvars)
# Determine value to compare address with # Determine value to compare address with
genvars_sum = [] genvars_sum = []
for i in range(self.dimensions): try:
if i != 0: for i, stride in enumerate(self.total_stride):
genvars_sum.append("+") genvars_sum.append(chr(97+i))
genvars_sum.append("*".join(map(str,self.array_dimensions[self.dimensions-i:])))
genvars_sum.append("*") genvars_sum.append("*")
genvars_sum.append(str(stride))
genvars_sum.append("+")
genvars_sum.append(chr(97+self.dimensions-1-i)) genvars_sum.pop()
self.logger.debug(
"Multidimensional with dimensions '{}' and stride '{}'".format(
self.total_array_dimensions,
self.total_stride))
except TypeError:
self.logger.debug(
"Caught expected TypeError because self.total_stride is empty")
except IndexError:
self.logger.debug(
"Caugt expected IndexError because genvars_sum is empty")
self.genvars_sum_str = ''.join(genvars_sum) self.genvars_sum_str = ''.join(genvars_sum)

View File

@ -142,6 +142,7 @@ singlepulse:
end end
out_port_assign: out_port_assign:
rtl: |- rtl: |-
// Connect register to hardware output port // Connect register to hardware output port
assign {path}_r{genvars} = {path}_q{genvars}; assign {path}_r{genvars} = {path}_q{genvars};
output_ports: output_ports:

View File

@ -0,0 +1,17 @@
---
regfile_comment:
rtl: |-
/*******************************************************************
*******************************************************************
* REGFILE : {name}
* DIMENSION : {dimensions}
* DEPTHS (per dimension): {depth}
*******************************************************************
*******************************************************************/
generate_for_start:
rtl: |-
for ({iterator} = 0; {iterator} < {limit}; {iterator}++)
begin
generate_for_end:
rtl: |-
end // of for loop with iterator {dimension}

View File

@ -19,7 +19,7 @@ rw_wire_assign_multi_dim:
rtl: | rtl: |
// Assign register-activation signals // Assign register-activation signals
assign {path}_reg_active{genvars} = addr == ({addr}+({genvars_sum})*{stride}); assign {path}_reg_active{genvars} = addr == {addr}+({genvars_sum});
assign {path}_sw_wr{genvars} = {path}_reg_active{genvars} && r_vld; assign {path}_sw_wr{genvars} = {path}_reg_active{genvars} && r_vld;
assign {path}_sw_rd{genvars} = {path}_reg_active{genvars} && w_vld; assign {path}_sw_rd{genvars} = {path}_reg_active{genvars} && w_vld;