Closes #13: I/O ports are now grouped and are tied to the register the belong to

This commit is contained in:
Dennis Potter 2021-11-26 16:15:00 -08:00
parent 9d05c90d50
commit ae83dceb7a
Signed by: Dennis
GPG Key ID: 186A8AD440942BAF
6 changed files with 158 additions and 117 deletions

View File

@ -153,81 +153,55 @@ class AddrMap(Component):
for name in self.get_resets() for name in self.get_resets()
] ]
ports_rtl = []
# Prefetch dictionaries in local array # Prefetch dictionaries in local array
input_dict_list = self.get_ports('input').items() port_dict_list = self.get_ports().items()
output_dict_list = self.get_ports('output').items()
input_signal_width = min( for group, ports in port_dict_list:
max([len(value[0]) for (_, value) in input_dict_list]), 40) ports_rtl.append(f"// Ports for '{group}'")
input_name_width = min( # Determine widths for this group
max([len(key) for (key, _) in input_dict_list]), 40) signal_width = max(
max([len(value.datatype) for (_, value) in ports.items()]), 12)
output_signal_width = min( name_width = max([len(key) for (key, _) in ports.items()])
max([len(value[0]) for (_, value) in output_dict_list]), 40)
output_name_width = min( # Generate RTL
max([len(key) for (key, _) in output_dict_list]), 40) for (key, port_type) in ports.items():
# Input ports
input_ports_rtl = []
for (key, value) in input_dict_list:
# TODO: Think about a better way to handle datatypes. Simply replacing them # TODO: Think about a better way to handle datatypes. Simply replacing them
# is not the most efficient way of handling it. # is not the most efficient way of handling it.
signal_type = value[0].replace('logic', '').strip() signal_type = port_type.datatype.replace('logic', '').strip()
if config['unpacked_arrays'] and value[1]: if config['unpacked_arrays'] and port_type.dim:
unpacked_dim = f"[{']['.join([str(y) for y in value[1]])}]" unpacked_dim = f"[{']['.join([str(y) for y in port_type.dim])}]"
elif value[1]: elif port_type.dim:
unpacked_dim = '' unpacked_dim = ''
signal_type = ''.join([ signal_type = ''.join([
f"[{':0]['.join([str(y-1) for y in value[1]])}:0]", f"[{':0]['.join([str(y-1) for y in port_type.dim])}:0]",
signal_type signal_type
]) ])
else: else:
unpacked_dim = '' unpacked_dim = ''
input_ports_rtl.append( ports_rtl.append(
AddrMap.templ_dict['input_port']['rtl'].format( AddrMap.templ_dict['port']['rtl'].format(
name = key, name = key,
direction = port_type.direction,
signal_type = signal_type, signal_type = signal_type,
signal_width = input_signal_width, signal_width = signal_width,
name_width = input_name_width, name_width = name_width,
unpacked_dim = unpacked_dim, unpacked_dim = unpacked_dim,
) )
) )
# Output ports # Append a new line after every port
output_ports_rtl = [] ports_rtl.append('')
for (key, value) in output_dict_list:
# TODO: Think about a better way to handle datatypes. Simply replacing them
# is not the most efficient way of handling it.
signal_type = value[0].replace('logic', '').strip()
if config['unpacked_arrays'] and value[1]:
unpacked_dim = f"[{']['.join([str(y) for y in value[1]])}]"
elif value[1]:
unpacked_dim = ''
signal_type = ''.join([
f"[{':0]['.join([str(y-1) for y in value[1]])}:0]",
signal_type
])
else:
unpacked_dim = ''
output_ports_rtl.append(
AddrMap.templ_dict['output_port']['rtl'].format(
name = key,
signal_type = signal_type,
signal_width = output_signal_width,
name_width = output_name_width,
unpacked_dim = unpacked_dim,
)
)
# Remove last newline
# Remove comma from last port entry # Remove comma from last port entry
output_ports_rtl[-1] = output_ports_rtl[-1].rstrip(',') ports_rtl.pop()
ports_rtl[-1] = ports_rtl[-1].rstrip(',')
# Define packages to be included. Always include the # Define packages to be included. Always include the
# b2w and w2b defines. # b2w and w2b defines.
@ -268,8 +242,7 @@ class AddrMap(Component):
name = self.name, name = self.name,
import_package_list = ''.join(import_package_list), import_package_list = ''.join(import_package_list),
resets = '\n'.join(reset_ports_rtl), resets = '\n'.join(reset_ports_rtl),
inputs = '\n'.join(input_ports_rtl), ports = '\n'.join(ports_rtl)))
outputs = '\n'.join(output_ports_rtl)))
# Add description, if applicable # Add description, if applicable
self.rtl_header.append(self.get_description()) self.rtl_header.append(self.get_description())
@ -321,8 +294,8 @@ class AddrMap(Component):
def __add_signal_instantiation(self): def __add_signal_instantiation(self):
dict_list = list(self.get_signals(True).items()) dict_list = list(self.get_signals(True).items())
signal_width = min(max([len(value[0]) for (_, value) in dict_list]), 40) signal_width = max(max([len(value.datatype) for (_, value) in dict_list]), 12)
name_width = min(max([len(key) for (key, _) in dict_list]), 40) name_width = max([len(key) for (key, _) in dict_list])
self.rtl_header = [ self.rtl_header = [
*self.rtl_header, *self.rtl_header,
@ -330,13 +303,13 @@ class AddrMap(Component):
'// Internal signals', '// Internal signals',
*[AddrMap.templ_dict['signal_declaration'].format( *[AddrMap.templ_dict['signal_declaration'].format(
name = key, name = key,
type = value[0], type = value.datatype,
signal_width = signal_width, signal_width = signal_width,
name_width = name_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.dim]))
if value[1] else '') if value.dim else '')
for (key, value) in dict_list], for (key, value) in dict_list],
'' ''
] ]

View File

@ -3,6 +3,7 @@ import math
import sys import sys
from typing import NamedTuple, Optional from typing import NamedTuple, Optional
from dataclasses import dataclass from dataclasses import dataclass
from enum import Enum
from systemrdl import node from systemrdl import node
@ -27,6 +28,15 @@ class SWMuxEntryDimensioned():
mux_entry: SWMuxEntry mux_entry: SWMuxEntry
dim: str dim: str
class SignalType(NamedTuple):
datatype: str
dim: list
class PortType(NamedTuple):
datatype: str
dim: list
direction: str # String "input" or "output"
class Component(): class Component():
def __init__( def __init__(
self, self,
@ -42,8 +52,6 @@ class Component():
self.ports = {} self.ports = {}
self.resets = set() self.resets = set()
self.signals = {} self.signals = {}
self.ports['input'] = {}
self.ports['output'] = {}
self.field_type = '' self.field_type = ''
# Save object # Save object
@ -146,13 +154,19 @@ class Component():
return self.resets return self.resets
def get_ports(self, port_type: str): def get_ports(self):
self.logger.debug("Return port list") self.logger.debug("Return port list")
for child in self.children.values(): for child in self.children.values():
self.ports[port_type] |= child.get_ports(port_type) for key, value in child.get_ports().items():
if key in self.ports:
self.logger.debug("Group '%s' already present in port list")
self.ports[key] |= value
else:
self.logger.debug("Adding group '%s' to port list")
self.ports |= {key: value}
return self.ports[port_type] return self.ports
def get_max_dim_depth(self) -> int: def get_max_dim_depth(self) -> int:
self.logger.debug("Return depth '%s' for dimensions (including parents) '%s'.", self.logger.debug("Return depth '%s' for dimensions (including parents) '%s'.",
@ -285,8 +299,12 @@ class Component():
name.append('_q') name.append('_q')
elif isinstance(obj, node.SignalNode): elif isinstance(obj, node.SignalNode):
# Must add it to signal list # Must add it to signal list
self.ports['input'][obj.inst_name] =\ self.ports['Signals'][obj.inst_name] =\
("logic" if obj.width == 1 else f"logic [{obj.width}:0]", []) PortType (
datatype = "logic" if obj.width == 1 else f"logic [{obj.width}:0]",
dim = [],
direction = "input"
)
else: else:
name.append('_') name.append('_')
name.append(obj.name) name.append(obj.name)
@ -325,8 +343,10 @@ class Component():
array_dimensions = self.total_array_dimensions array_dimensions = self.total_array_dimensions
self.signals[signal['name'].format(**values)] =\ self.signals[signal['name'].format(**values)] =\
(signal['signal_type'].format(**values), SignalType (
array_dimensions) datatype = signal['signal_type'].format(**values),
dim = array_dimensions,
)
except (TypeError, KeyError): except (TypeError, KeyError):
pass pass
@ -341,9 +361,29 @@ class Component():
except KeyError: except KeyError:
array_dimensions = self.total_array_dimensions array_dimensions = self.total_array_dimensions
self.ports['input'][input_p['name'].format(**values)] =\ try:
(input_p['signal_type'].format(**values), group = input_p['group'].format(**values)
array_dimensions) except KeyError:
group = self.path_underscored_wo_field
name = input_p['name'].format(**values)
if group not in self.ports:
self.ports[group] = {
name:
PortType (
datatype = input_p['signal_type'].format(**values),
dim = array_dimensions,
direction = "input",
)
}
elif name not in self.ports[group]:
self.ports[group][name] =\
PortType (
datatype = input_p['signal_type'].format(**values),
dim = array_dimensions,
direction = "input",
)
except (TypeError, KeyError): except (TypeError, KeyError):
pass pass
@ -358,9 +398,29 @@ class Component():
except KeyError: except KeyError:
array_dimensions = self.total_array_dimensions array_dimensions = self.total_array_dimensions
self.ports['output'][output_p['name'].format(**values)] =\ try:
(output_p['signal_type'].format(**values), group = output_p['group'].format(**values)
array_dimensions) except KeyError:
group = self.path_underscored_wo_field
name = output_p['name'].format(**values)
if group not in self.ports:
self.ports[group] = {
name:
PortType (
datatype = output_p['signal_type'].format(**values),
dim = array_dimensions,
direction = "output",
)
}
elif name not in self.ports[group]:
self.ports[group][name] =\
PortType (
datatype = output_p['signal_type'].format(**values),
dim = array_dimensions,
direction = "output",
)
except (TypeError, KeyError): except (TypeError, KeyError):
pass pass
@ -371,6 +431,9 @@ class Component():
self.owning_addrmap, self.full_path, self.path, self.path_underscored =\ self.owning_addrmap, self.full_path, self.path, self.path_underscored =\
Component.create_underscored_path_static(self.obj) Component.create_underscored_path_static(self.obj)
# By default, this is identical to path_underscored. Fields will override this
self.path_underscored_wo_field = self.path_underscored
@staticmethod @staticmethod
def create_underscored_path_static(obj): def create_underscored_path_static(obj):
owning_addrmap = obj.owning_addrmap.inst_name owning_addrmap = obj.owning_addrmap.inst_name

View File

@ -110,10 +110,11 @@ class Field(Component):
else: else:
path = self.path path = self.path
path_wo_field = '__'.join(path.split('.', -1)[0:-1]) # This is different than self.path_underscored_wo_field
path_underscored_wo_field = '__'.join(path.split('.', -1)[0:-1])
# path_wo_field_vec & path_undrescored_vec only used for external registers # path_wo_field_vec & path_undrescored_vec only used for external registers
self.path_wo_field_vec.append(path_wo_field) self.path_wo_field_vec.append(path_underscored_wo_field)
self.path_underscored_vec.append(alias_path_underscored if alias else self.path_underscored) self.path_underscored_vec.append(alias_path_underscored if alias else self.path_underscored)
# Define software access (if applicable) # Define software access (if applicable)
@ -121,7 +122,7 @@ class Field(Component):
if self.properties['sw_wr']: if self.properties['sw_wr']:
# Append to list of registers that can write # Append to list of registers that can write
self.writable_by.add(path_wo_field) self.writable_by.add(path_underscored_wo_field)
# This will need a wire to indicate that a write is taking place # This will need a wire to indicate that a write is taking place
self.properties['sw_wr_wire'] = True self.properties['sw_wr_wire'] = True
@ -133,7 +134,7 @@ class Field(Component):
access_rtl['sw_write'][0].append( access_rtl['sw_write'][0].append(
self._process_yaml( self._process_yaml(
Field.templ_dict['sw_access_field_swwe'], Field.templ_dict['sw_access_field_swwe'],
{'path_wo_field': path_wo_field, {'path_wo_field': path_underscored_wo_field,
'genvars': self.genvars_str, 'genvars': self.genvars_str,
'swwe': self.get_signal_name(swwe), 'swwe': self.get_signal_name(swwe),
'field_type': self.field_type} 'field_type': self.field_type}
@ -143,7 +144,7 @@ class Field(Component):
access_rtl['sw_write'][0].append( access_rtl['sw_write'][0].append(
self._process_yaml( self._process_yaml(
Field.templ_dict['sw_access_field_swwel'], Field.templ_dict['sw_access_field_swwel'],
{'path_wo_field': path_wo_field, {'path_wo_field': path_underscored_wo_field,
'genvars': self.genvars_str, 'genvars': self.genvars_str,
'swwel': self.get_signal_name(swwel), 'swwel': self.get_signal_name(swwel),
'field_type': self.field_type} 'field_type': self.field_type}
@ -153,7 +154,7 @@ class Field(Component):
access_rtl['sw_write'][0].append( access_rtl['sw_write'][0].append(
self._process_yaml( self._process_yaml(
Field.templ_dict['sw_access_field'], Field.templ_dict['sw_access_field'],
{'path_wo_field': path_wo_field, {'path_wo_field': path_underscored_wo_field,
'genvars': self.genvars_str, 'genvars': self.genvars_str,
'field_type': self.field_type} 'field_type': self.field_type}
) )
@ -211,7 +212,7 @@ class Field(Component):
if obj.get_property('sw') in (AccessType.rw, AccessType.r): if obj.get_property('sw') in (AccessType.rw, AccessType.r):
# Append to list of registers that can read # Append to list of registers that can read
self.readable_by.add(path_wo_field) self.readable_by.add(path_underscored_wo_field)
self.properties['sw_rd'] = True self.properties['sw_rd'] = True
@ -224,7 +225,7 @@ class Field(Component):
access_rtl['sw_read'][0].append( access_rtl['sw_read'][0].append(
self._process_yaml( self._process_yaml(
Field.templ_dict['sw_read_access_field'], Field.templ_dict['sw_read_access_field'],
{'path_wo_field': path_wo_field, {'path_wo_field': path_underscored_wo_field,
'genvars': self.genvars_str, 'genvars': self.genvars_str,
'field_type': self.field_type} 'field_type': self.field_type}
) )
@ -711,7 +712,7 @@ class Field(Component):
self._process_yaml( self._process_yaml(
Field.templ_dict['swmod_assign'], Field.templ_dict['swmod_assign'],
{'path': self.path_underscored, {'path': self.path_underscored,
'path_wo_field': self.path_wo_field, 'path_wo_field': self.path_underscored_wo_field,
'genvars': self.genvars_str, 'genvars': self.genvars_str,
'rd_wr': 'rd', 'rd_wr': 'rd',
'msbyte': self.msbyte, 'msbyte': self.msbyte,
@ -727,7 +728,7 @@ class Field(Component):
self._process_yaml( self._process_yaml(
Field.templ_dict['swmod_assign'], Field.templ_dict['swmod_assign'],
{'path': self.path_underscored, {'path': self.path_underscored,
'path_wo_field': self.path_wo_field, 'path_wo_field': self.path_underscored_wo_field,
'genvars': self.genvars_str, 'genvars': self.genvars_str,
'rd_wr': 'wr', 'rd_wr': 'wr',
'msbyte': self.msbyte, 'msbyte': self.msbyte,
@ -762,7 +763,7 @@ class Field(Component):
swacc_props = self._process_yaml( swacc_props = self._process_yaml(
Field.templ_dict['swacc_assign'], Field.templ_dict['swacc_assign'],
{'path': self.path_underscored, {'path': self.path_underscored,
'path_wo_field': self.path_wo_field, 'path_wo_field': self.path_underscored_wo_field,
'genvars': self.genvars_str, 'genvars': self.genvars_str,
'msbyte': self.msbyte, 'msbyte': self.msbyte,
'lsbyte': self.lsbyte, 'lsbyte': self.lsbyte,
@ -1349,7 +1350,7 @@ class Field(Component):
self, self,
obj: FieldNode): obj: FieldNode):
# Create full name # Create full name
self.path_wo_field = '__'.join(self.path.split('.', -1)[0:-1]) self.path_underscored_wo_field = '__'.join(self.path.split('.', -1)[0:-1])
self.register_name = ''.join([self.path_underscored, '_q']) self.register_name = ''.join([self.path_underscored, '_q'])
self.path_underscored_vec = [] self.path_underscored_vec = []
@ -1451,7 +1452,7 @@ class Field(Component):
external = self.config['external'], external = self.config['external'],
lsb = self.obj.lsb, lsb = self.obj.lsb,
msb = self.obj.msb, msb = self.obj.msb,
path_wo_field = self.path_wo_field, path_wo_field = self.path_underscored_wo_field,
storage_type = self.storage_type, storage_type = self.storage_type,
) )

View File

@ -511,20 +511,18 @@ class Register(Component):
def get_signal_instantiations_list(self): def get_signal_instantiations_list(self):
dict_list = list(self.get_signals().items()) dict_list = list(self.get_signals().items())
signal_width = max(max([len(value.datatype) for (_, value) in dict_list]), 12)
signal_width = min(max([len(value[0]) for (_, value) in dict_list]), 40) name_width = max([len(key) for (key, _) in dict_list])
name_width = min(max([len(key) for (key, _) in dict_list]), 40)
return [Register.templ_dict['signal_declaration'].format( return [Register.templ_dict['signal_declaration'].format(
name = key, name = key,
type = value[0], type = value.datatype,
signal_width = signal_width, signal_width = signal_width,
name_width = name_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.dim]))
if value[1] else '') if value.dim else '')
for (key, value) in dict_list] for (key, value) in dict_list]
def add_alias(self, obj: node.RegNode): def add_alias(self, obj: node.RegNode):

View File

@ -75,14 +75,11 @@ module_declaration:
<<UNINDENT>> <<UNINDENT>>
( (
<<INDENT>> <<INDENT>>
// Resets // Reset signals declared for registers
{resets} {resets}
// Inputs {ports}
{inputs}
// Outputs
{outputs}
<<UNINDENT>> <<UNINDENT>>
); );
import_package: import_package:
@ -91,12 +88,9 @@ import_package:
reset_port: reset_port:
rtl: rtl:
input {name}, input {name},
input_port: port:
rtl: rtl: |-
input {signal_type:{signal_width}} {name:{name_width}}{unpacked_dim}, {direction:6} {signal_type:{signal_width}} {name:{name_width}}{unpacked_dim},
output_port:
rtl:
output {signal_type:{signal_width}} {name:{name_width}}{unpacked_dim},
signal_declaration: |- signal_declaration: |-
{type:{signal_width}} {name:{name_width}}{unpacked_dim}; {type:{signal_width}} {name:{name_width}}{unpacked_dim};
package_declaration: package_declaration:

View File

@ -40,26 +40,38 @@ module_instantiation:
input_ports: input_ports:
- name: 'clk' - name: 'clk'
signal_type: '' signal_type: ''
group: 'General Clock'
- name: 'HRESETn' - name: 'HRESETn'
signal_type: '' signal_type: ''
group: 'AHB Protocol'
- name: 'HADDR' - name: 'HADDR'
signal_type: '[31:0]' signal_type: '[31:0]'
group: 'AHB Protocol'
- name: 'HWRITE' - name: 'HWRITE'
signal_type: '' signal_type: ''
group: 'AHB Protocol'
- name: 'HSIZE' - name: 'HSIZE'
signal_type: '[2:0]' signal_type: '[2:0]'
group: 'AHB Protocol'
- name: 'HPROT' - name: 'HPROT'
signal_type: '[3:0]' signal_type: '[3:0]'
group: 'AHB Protocol'
- name: 'HTRANS' - name: 'HTRANS'
signal_type: '[1:0]' signal_type: '[1:0]'
group: 'AHB Protocol'
- name: 'HWDATA' - name: 'HWDATA'
signal_type: '[{bus_width}-1:0]' signal_type: '[{bus_width}-1:0]'
group: 'AHB Protocol'
- name: 'HSEL' - name: 'HSEL'
signal_type: '' signal_type: ''
group: 'AHB Protocol'
output_ports: output_ports:
- name: 'HREADYOUT' - name: 'HREADYOUT'
signal_type: '' signal_type: ''
group: 'AHB Protocol'
- name: 'HRESP' - name: 'HRESP'
signal_type: '' signal_type: ''
group: 'AHB Protocol'
- name: 'HRDATA' - name: 'HRDATA'
signal_type: '[{bus_width}-1:0]' signal_type: '[{bus_width}-1:0]'
group: 'AHB Protocol'