Add support for enumeration encoding

Fields that are encoded as enumerations are now recognized by the
application. All relevant information will be saved in the Field Object
and the variables and I/O list will be generated accordingly.

This commit also adds dynamic padding of the I/O and variable lists.

Still lacking is the automatic generation of SV packages.
This commit is contained in:
Dennis Potter 2021-05-24 11:41:45 +02:00
parent 085e2ea2dc
commit b7c1a12179
Signed by: Dennis
GPG Key ID: 186A8AD440942BAF
6 changed files with 114 additions and 22 deletions

View File

@ -59,28 +59,49 @@ class AddrMap(Component):
# 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/inout ports")
# Prefetch dictionaries in local array
input_dict_list = [(key, value) for (key, value) in self.get_ports('input').items()]
output_dict_list = [(key, value) for (key, value) in self.get_ports('output').items()]
input_signal_width = min(
max([len(value[0]) for (_, value) in input_dict_list]), 40)
input_name_width = min(
max([len(key) for (key, _) in input_dict_list]), 40)
output_signal_width = min(
max([len(value[0]) for (_, value) in output_dict_list]), 40)
output_name_width = min(
max([len(key) for (key, _) in output_dict_list]), 40)
# Input ports # Input ports
input_ports_rtl = [ input_ports_rtl = [
AddrMap.templ_dict['input_port'].format( AddrMap.templ_dict['input_port'].format(
name = key, name = key,
signal_type = value[0], signal_type = value[0],
signal_width = input_signal_width,
name_width = input_name_width,
unpacked_dim = '[{}]'.format( unpacked_dim = '[{}]'.format(
']['.join( ']['.join(
[str(y) for y in value[1]])) [str(y) for y in value[1]]))
if value[1] else '') if value[1] else '')
for (key, value) in self.get_ports('input').items() for (key, value) in input_dict_list
] ]
# Output ports # Output ports
output_ports_rtl = [ output_ports_rtl = [
AddrMap.templ_dict['output_port'].format( AddrMap.templ_dict['output_port'].format(
name = key, name = key,
signal_width = output_signal_width,
name_width = output_name_width,
signal_type = value[0], signal_type = value[0],
unpacked_dim = '[{}]'.format( unpacked_dim = '[{}]'.format(
']['.join( ']['.join(
[str(y) for y in value[1]])) [str(y) for y in value[1]]))
if value[1] else '') if value[1] else '')
for (key, value) in self.get_ports('output').items() for (key, value) in output_dict_list
] ]
# Remove comma from last port entry # Remove comma from last port entry

View File

@ -7,19 +7,16 @@ from systemrdl import node
from log.log import create_logger from log.log import create_logger
# Define NamedTuple # Define NamedTuple
class TypeDefMembers(NamedTuple):
name: str
member_type: str
class TypeDef(NamedTuple): class TypeDef(NamedTuple):
name: str scope: str
members: list[TypeDefMembers] members: tuple
class Component(): class Component():
def __init__(self): def __init__(self):
self.rtl_header = [] self.rtl_header = []
self.rtl_footer = [] self.rtl_footer = []
self.children = [] self.children = []
self.typedef = dict()
self.ports = dict() self.ports = dict()
self.signals = dict() self.signals = dict()
self.ports['input'] = dict() self.ports['input'] = dict()

View File

@ -2,12 +2,12 @@ import math
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.node import FieldNode, SignalNode
from systemrdl.node import FieldNode from systemrdl.component import Reg, Regfile, Addrmap, Root
from systemrdl.rdltypes import PrecedenceType, AccessType, OnReadType, OnWriteType from systemrdl.rdltypes import PrecedenceType, AccessType, OnReadType, OnWriteType
# Local modules # Local modules
from components.component import Component from components.component import Component, TypeDef
from . import templates from . import templates
class Field(Component): class Field(Component):
@ -16,7 +16,7 @@ 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: node.FieldNode, array_dimensions: list, config:dict): def __init__(self, obj: FieldNode, array_dimensions: list, config:dict):
super().__init__() super().__init__()
# Save and/or process important variables # Save and/or process important variables
@ -26,6 +26,9 @@ class Field(Component):
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 field "{}"'.format(obj.inst_name)) self.logger.debug('Starting to process field "{}"'.format(obj.inst_name))
# Determine field types
self.__process_fieldtype()
################################################################################## ##################################################################################
# LIMITATION: # LIMITATION:
# v1.x of the systemrdl-compiler does not support non-homogeneous arrays. # v1.x of the systemrdl-compiler does not support non-homogeneous arrays.
@ -67,7 +70,73 @@ class Field(Component):
[self.yaml_signals_to_list(Field.templ_dict[i[1]]) for i in operations] [self.yaml_signals_to_list(Field.templ_dict[i[1]]) for i in operations]
def __process_variables(self, obj: node.RootNode, array_dimensions: list): def __process_fieldtype(self):
try:
enum = self.obj.get_property('encode')
# Rules for scope:
# - Regfiles or addrmaps have packages
# - An enum that is not defined within a register will go into the package
# of the first addrmap or regfile that is found when iterating through
# the parents
# - Regfiles don't need to be unique in a design. Therefore, the packages of
# regfiles shall be prepended by the addrmap name.
# - When the enum is defined in a register, that register will be prepended
# to the name of that enum.
#
# This procedure is expensive, but None.parent() will not work and therefore
# kill the try block in most cases
parent_scope = enum.get_parent_scope()
self.logger.debug("Starting to parse '{}'".format(enum))
if isinstance(parent_scope, Reg):
enum_name = '__'.join([enum.get_scope_path().split('::')[-1], enum.__name__])
parent_scope = parent_scope.parent_scope
else:
enum_name = enum.__name__
path = []
# Open up all parent scopes and append it to scope list
while 1:
if isinstance(parent_scope, Regfile):
if parent_scope.is_instance:
path.append(parent_scope.inst_name)
else:
path.append(parent_scope.type_name)
# That's a lot of parent_scope's...
parent_scope = parent_scope.parent_scope
else:
path.append(self.owning_addrmap)
break
# Create string. Reverse list so that order starts at addrmap
scope = '__'.join(reversed(path))
# Create internal NamedTuple with information on Enum
self.typedef[enum_name] = TypeDef (
scope=scope,
members= [(x.name, x.value) for x in self.obj.get_property('encode')]
)
# Save name of object
self.field_type =\
'::'.join([scope, enum_name])
self.logger.info("Parsed enum '{}'".format(enum_name))
except AttributeError:
# In case of an AttributeError, the encode property is None. Hence,
# the field has a simple width
if self.obj.width > 1:
self.field_type = 'logic [{}:0]'.format(self.obj.width-1)
else:
self.field_type = 'logic'
def __process_variables(self, obj: FieldNode, array_dimensions: list):
# Save object # Save object
self.obj = obj self.obj = obj
@ -81,9 +150,6 @@ class Field(Component):
self.path_underscored = self.path.replace('.', '_') 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])
# Field type
self.field_type = 'logic'
# Save dimensions of unpacked dimension # Save dimensions of unpacked dimension
self.array_dimensions = array_dimensions self.array_dimensions = array_dimensions
@ -228,13 +294,13 @@ class Field(Component):
swwe = self.obj.get_property('swwe') swwe = self.obj.get_property('swwe')
swwel = self.obj.get_property('swwel') swwel = self.obj.get_property('swwel')
if isinstance(swwe, (node.FieldNode, node.SignalNode)): if isinstance(swwe, (FieldNode, SignalNode)):
access_rtl['sw_write'].append( access_rtl['sw_write'].append(
Field.templ_dict['sw_access_field_swwe']['rtl'].format( Field.templ_dict['sw_access_field_swwe']['rtl'].format(
path_wo_field = self.path_wo_field, path_wo_field = self.path_wo_field,
genvars = self.genvars_str, genvars = self.genvars_str,
swwe = Component.get_signal_name(swwe))) swwe = Component.get_signal_name(swwe)))
elif isinstance(swwel, (node.FieldNode, node.SignalNode)): elif isinstance(swwel, (FieldNode, SignalNode)):
access_rtl['sw_write'].append( access_rtl['sw_write'].append(
Field.templ_dict['sw_access_field_swwel']['rtl'].format( Field.templ_dict['sw_access_field_swwel']['rtl'].format(
path_wo_field = self.path_wo_field, path_wo_field = self.path_wo_field,

View File

@ -70,16 +70,24 @@ class Register(Component):
self.yaml_signals_to_list(Register.templ_dict[rw_wire_assign_field]) self.yaml_signals_to_list(Register.templ_dict[rw_wire_assign_field])
# Add wire/register instantiations # Add wire/register instantiations
dict_list = [(key, value) for (key, value) in self.get_signals().items()]
signal_width = min(max([len(value[0]) for (_, value) in dict_list]), 40)
name_width = min(max([len(key) for (key, _) in dict_list]), 40)
self.rtl_header = [ self.rtl_header = [
*[ *[
Register.templ_dict['signal_declaration'].format( Register.templ_dict['signal_declaration'].format(
name = key, name = key,
type = value[0], type = value[0],
signal_width = signal_width,
name_width = name_width,
unpacked_dim = '[{}]'.format( unpacked_dim = '[{}]'.format(
']['.join( ']['.join(
[str(y) for y in value[1]])) [str(y) for y in value[1]]))
if value[1] else '') if value[1] else '')
for (key, value) in self.get_signals().items()], for (key, value) in dict_list],
'', '',
*self.rtl_header, *self.rtl_header,
] ]

View File

@ -11,6 +11,6 @@ module_declaration: |-
{outputs} {outputs}
); );
input_port: |- input_port: |-
input {signal_type:15s}{name:25s} {unpacked_dim}, input {signal_type:{signal_width}} {name:{name_width}}{unpacked_dim},
output_port: |- output_port: |-
output {signal_type:15s}{name:25s} {unpacked_dim}, output {signal_type:{signal_width}} {name:{name_width}}{unpacked_dim},

View File

@ -47,4 +47,4 @@ generate_for_start: |-
generate_for_end: |- generate_for_end: |-
end // of for loop with iterator {dimension} end // of for loop with iterator {dimension}
signal_declaration: |- signal_declaration: |-
{type:10s}{name:20s} {unpacked_dim}; {type:{signal_width}} {name:{name_width}}{unpacked_dim};