Add read-multiplexer logic

Evaluating every single wire is maybe not the best way to do this, but
it is probably better than writing a very exotic SV construct with
(for-)loops, breaks, and a lot of conditions. Most synthesis tools are
pretty good at recognizing this case-construct and generating a good
mutliplexer.
This commit is contained in:
Dennis Potter 2021-06-23 01:03:11 +02:00
parent b2c756af41
commit 32c6fc3c4a
Signed by: Dennis
GPG Key ID: 186A8AD440942BAF
8 changed files with 175 additions and 21 deletions

View File

@ -69,6 +69,9 @@ class AddrMap(Component):
self.logger.info("Done generating all child-regfiles/registers") self.logger.info("Done generating all child-regfiles/registers")
# Create RTL of all registers
[x.create_rtl() for x in self.registers.values()]
# Add bus widget ports # Add bus widget ports
widget_rtl = self.__get_widget_ports_rtl() widget_rtl = self.__get_widget_ports_rtl()
@ -159,9 +162,32 @@ class AddrMap(Component):
# Append genvars # Append genvars
self.__append_genvars() self.__append_genvars()
# Create read multiplexer
self.__create_mux_string()
# Add endmodule keyword # Add endmodule keyword
self.rtl_footer.append('endmodule') self.rtl_footer.append('endmodule')
def __create_mux_string(self):
# TODO: Add variable for bus width
self.rtl_footer.append(
self.process_yaml(
AddrMap.templ_dict['read_mux'],
{'list_of_cases':
'\n'.join([
AddrMap.templ_dict['list_of_mux_cases']['rtl']
.format(x[0][1]+x[1][0],
''.join(
[x[0][0],
x[1][1]])) for y in self.children.values() \
for x in y.create_mux_string()
])
}
)
)
def __add_signal_instantiation(self): def __add_signal_instantiation(self):
dict_list = [(key, value) for (key, value) in self.get_signals(True).items()] dict_list = [(key, value) for (key, value) in self.get_signals(True).items()]
signal_width = min(max([len(value[0]) for (_, value) in dict_list]), 40) signal_width = min(max([len(value[0]) for (_, value) in dict_list]), 40)

View File

@ -26,6 +26,7 @@ class Component():
self.field_type = '' self.field_type = ''
# Save object # Save object
# TODO: should probably be list because of alias registers
self.obj = obj self.obj = obj
# Save name # Save name
@ -100,14 +101,6 @@ class Component():
rtl_children = [] rtl_children = []
for x in self.children.values(): for x in self.children.values():
# In case of fields, the RTL must first be generated.
# Reason is that we only know at the very end whether
# all alias registers are processed for fields
try:
x.create_rtl()
except:
pass
rtl_children.append(x.get_rtl()) rtl_children.append(x.get_rtl())
# Concatenate header, main, and footer # Concatenate header, main, and footer

View File

@ -155,10 +155,14 @@ class Field(Component):
onread = obj.get_property('onread') onread = obj.get_property('onread')
access_rtl['sw_read'] = ([], False) access_rtl['sw_read'] = ([], False)
if obj.get_property('sw') in (AccessType.rw, AccessType.r) and onread: if obj.get_property('sw') in (AccessType.rw, AccessType.r):
# Append to list of registers that can read
self.readable_by.add(path_wo_field)
# Set onread properties
if onread == OnReadType.ruser: if onread == OnReadType.ruser:
self.logger.warning("The OnReadType.ruser is not yet supported!") self.logger.error("The OnReadType.ruser is not yet supported!")
else: elif onread:
access_rtl['sw_read'][0].append( access_rtl['sw_read'][0].append(
self.process_yaml( self.process_yaml(
Field.templ_dict[str(onread)], Field.templ_dict[str(onread)],
@ -412,6 +416,11 @@ class Field(Component):
# Save byte boundaries # Save byte boundaries
self.lsbyte = math.floor(obj.inst.lsb / 8) self.lsbyte = math.floor(obj.inst.lsb / 8)
self.msbyte = math.floor(obj.inst.msb / 8) self.msbyte = math.floor(obj.inst.msb / 8)
self.msb = obj.inst.msb
self.lsb = obj.inst.lsb
# Set that tells which hierarchies can read this field
self.readable_by = set()
# Determine resets. This includes checking for async/sync resets, # Determine resets. This includes checking for async/sync resets,
# the reset value, and whether the field actually has a reset # the reset value, and whether the field actually has a reset

View File

@ -122,6 +122,9 @@ class RegFile(Component):
glbl_settings['generate_active'] = False glbl_settings['generate_active'] = False
self.rtl_footer.append("endgenerate") self.rtl_footer.append("endgenerate")
# Create RTL of all registers
[x.create_rtl() for x in self.registers.values()]
def __process_variables(self, def __process_variables(self,
obj: node.RegfileNode, obj: node.RegfileNode,
parents_dimensions: list, parents_dimensions: list,
@ -160,4 +163,7 @@ class RegFile(Component):
genvars = ['[{}]'.format(chr(97+i)) for i in range(self.dimensions)] genvars = ['[{}]'.format(chr(97+i)) for i in range(self.dimensions)]
self.genvars_str = ''.join(genvars) self.genvars_str = ''.join(genvars)
def create_mux_string(self):
for i in self.children.values():
yield from i.create_mux_string()

View File

@ -2,6 +2,7 @@ import importlib.resources as pkg_resources
import math import math
import sys import sys
import yaml import yaml
import itertools
from systemrdl import node from systemrdl import node
@ -28,8 +29,7 @@ class Register(Component):
# Save and/or process important variables # Save and/or process important variables
self.__process_variables(obj, parents_dimensions, parents_stride, glbl_settings) self.__process_variables(obj, parents_dimensions, parents_stride, glbl_settings)
# Create RTL for fields # Create RTL for fields of initial, non-alias register
# Fields should be in order in RTL, therefore, use list
for field in obj.fields(): for field in obj.fields():
# Use range to save field in an array. Reason is, names are allowed to # Use range to save field in an array. Reason is, names are allowed to
# change when using an alias # change when using an alias
@ -44,6 +44,9 @@ class Register(Component):
self.children[field_range].sanity_checks() self.children[field_range].sanity_checks()
def create_rtl(self): def create_rtl(self):
# Create RTL of children
[x.create_rtl() for x in self.children.values()]
# Create generate block for register and add comment # Create generate block for register and add comment
if self.dimensions and not self.generate_active: if self.dimensions and not self.generate_active:
self.rtl_header.append("generate") self.rtl_header.append("generate")
@ -69,15 +72,89 @@ class Register(Component):
if self.dimensions and not self.generate_active: if self.dimensions and not self.generate_active:
self.rtl_footer.append("endgenerate\n") self.rtl_footer.append("endgenerate\n")
# Add assignment of read-wires
self.__add_sw_read_assignments()
# Add wire instantiation
self.__add_signal_instantiations()
# Create comment and provide user information about register he/she is looking at # Create comment and provide user information about register he/she is looking at
self.rtl_header = [ self.rtl_header = [
Register.templ_dict['reg_comment'].format( Register.templ_dict['reg_comment'].format(
name = obj.inst_name, name = self.obj.inst_name,
dimensions = self.dimensions, dimensions = self.dimensions,
depth = self.depth), depth = self.depth),
*self.rtl_header *self.rtl_header
] ]
def __add_sw_read_assignments(self):
accesswidth = self.obj.get_property('accesswidth') - 1
self.rtl_footer.append("")
for x in self.name_addr_mappings:
current_bit = 0
list_of_fields = []
for y in self.children.values():
if x[0] in y.readable_by:
empty_bits = y.lsb - current_bit
current_bit = y.msb + 1
if empty_bits > 0:
list_of_fields.append("{}'b0".format(empty_bits))
list_of_fields.append("{}_q".format(y.path_underscored))
empty_bits = accesswidth - current_bit + 1
if empty_bits > 0:
list_of_fields.append("{}'b0".format(empty_bits))
# Create list of mux-inputs to later be picked up by carrying addrmap
self.sw_read_assignment_var_name.append(
(
self.process_yaml(
Register.templ_dict['sw_read_assignment_var_name'],
{'path': x[0],
'accesswidth': accesswidth}
),
x[1], # Start addr
)
)
self.rtl_footer.append(
self.process_yaml(
Register.templ_dict['sw_read_assignment'],
{'sw_read_assignment_var_name': self.sw_read_assignment_var_name[-1][0],
'genvars': self.genvars_str,
'list_of_fields': ', '.join(reversed(list_of_fields))}
)
)
def create_mux_string(self):
for mux_tuple in self.sw_read_assignment_var_name:
# Loop through lowest dimension and add stride of higher
# dimension once everything is processed
if self.total_array_dimensions:
vec = [0]*len(self.total_array_dimensions)
for i in self.eval_genvars(vec, 0, self.total_array_dimensions):
yield (mux_tuple, i)
def eval_genvars(self, vec, depth, dimensions):
for i in range(dimensions[depth]):
vec[depth] = i
if depth == len(dimensions) - 1:
yield (
eval(self.genvars_sum_str_vectorized),
'[{}]'.format(']['.join(map(str, vec)))
)
else:
yield from self.eval_genvars(vec, depth+1, dimensions)
vec[depth] = 0
def __add_address_decoder(self): def __add_address_decoder(self):
# Assign variables from bus # Assign variables from bus
self.obj.current_idx = [0] self.obj.current_idx = [0]
@ -98,8 +175,9 @@ class Register(Component):
'depth': self.depth, 'depth': self.depth,
'field_type': self.field_type} 'field_type': self.field_type}
) )
) for i, x in enumerate(self.alias_names)] ) for i, x in enumerate(self.name_addr_mappings)]
def __add_signal_instantiations(self):
# Add wire/register instantiations # Add wire/register instantiations
dict_list = [(key, value) for (key, value) in self.get_signals().items()] dict_list = [(key, value) for (key, value) in self.get_signals().items()]
@ -143,7 +221,8 @@ class Register(Component):
# Add name to list # Add name to list
self.obj.current_idx = [0] self.obj.current_idx = [0]
self.alias_names.append((self.create_underscored_path_static(obj)[3], obj.absolute_address)) self.name_addr_mappings.append(
(self.create_underscored_path_static(obj)[3], obj.absolute_address))
def __process_variables( def __process_variables(
self, self,
@ -151,13 +230,15 @@ class Register(Component):
parents_dimensions: list, parents_dimensions: list,
parents_stride: list, parents_stride: list,
glbl_settings: dict): glbl_settings: dict):
# Save object
self.obj = obj
# Save name # Save name
self.obj.current_idx = [0] self.obj.current_idx = [0]
self.name = obj.inst_name self.name = obj.inst_name
self.alias_names = [(self.create_underscored_path_static(obj)[3], obj.absolute_address)]
# Create mapping between (alias-) name and address
self.name_addr_mappings = [
(self.create_underscored_path_static(obj)[3], obj.absolute_address)
]
# Create full name # Create full name
self.create_underscored_path() self.create_underscored_path()
@ -165,6 +246,9 @@ class Register(Component):
# Gnerate already started? # Gnerate already started?
self.generate_active = glbl_settings['generate_active'] self.generate_active = glbl_settings['generate_active']
# Empty array for mux-input signals
self.sw_read_assignment_var_name = []
# Determine dimensions of register # Determine dimensions of register
if obj.is_array: if obj.is_array:
self.sel_arr = 'array' self.sel_arr = 'array'
@ -183,7 +267,7 @@ class Register(Component):
self.sel_arr = 'single' self.sel_arr = 'single'
self.total_array_dimensions = parents_dimensions self.total_array_dimensions = parents_dimensions
self.array_dimensions = [] self.array_dimensions = []
self.total_stride = self.obj.array_stride self.total_stride = parents_stride
# How many dimensions were already part of some higher up hierarchy? # How many dimensions were already part of some higher up hierarchy?
self.parents_depths = len(parents_dimensions) self.parents_depths = len(parents_dimensions)
@ -200,6 +284,7 @@ class Register(Component):
# Determine value to compare address with # Determine value to compare address with
genvars_sum = [] genvars_sum = []
genvars_sum_vectorized = []
try: try:
for i, stride in enumerate(self.total_stride): for i, stride in enumerate(self.total_stride):
genvars_sum.append(chr(97+i)) genvars_sum.append(chr(97+i))
@ -207,7 +292,14 @@ class Register(Component):
genvars_sum.append(str(stride)) genvars_sum.append(str(stride))
genvars_sum.append("+") genvars_sum.append("+")
genvars_sum_vectorized.append('vec[')
genvars_sum_vectorized.append(str(i))
genvars_sum_vectorized.append(']*')
genvars_sum_vectorized.append(str(stride))
genvars_sum_vectorized.append("+")
genvars_sum.pop() genvars_sum.pop()
genvars_sum_vectorized.pop()
self.logger.debug( self.logger.debug(
"Multidimensional with dimensions '{}' and stride '{}'".format( "Multidimensional with dimensions '{}' and stride '{}'".format(
@ -221,4 +313,5 @@ class Register(Component):
"Caugt expected IndexError because genvars_sum is empty") "Caugt expected IndexError because genvars_sum is empty")
self.genvars_sum_str = ''.join(genvars_sum) self.genvars_sum_str = ''.join(genvars_sum)
self.genvars_sum_str_vectorized = ''.join(genvars_sum_vectorized)

View File

@ -48,3 +48,16 @@ enum_declaration:
enum_var_list_item: enum_var_list_item:
rtl: |- rtl: |-
{name:{max_name_width}} = {width}'d{value} {name:{max_name_width}} = {width}'d{value}
read_mux:
rtl: |-
// Read multiplexer
always_comb
begin
case(addr)
{list_of_cases}
endcase
end
list_of_mux_cases:
rtl: |-
32'd{}: sw_rd_bus = {};

View File

@ -15,7 +15,7 @@ rw_wire_assign_1_dim:
input_ports: input_ports:
output_ports: output_ports:
rw_wire_assign_multi_dim: rw_wire_assign_multi_dim:
rtl: | rtl: |-
// Register-activation for '{path}' {alias} // Register-activation for '{path}' {alias}
assign {path}_accss{genvars} = addr == {addr}+({genvars_sum}); assign {path}_accss{genvars} = addr == {addr}+({genvars_sum});
assign {path}_sw_wr{genvars} = {path}_accss{genvars} && r_vld; assign {path}_sw_wr{genvars} = {path}_accss{genvars} && r_vld;
@ -45,3 +45,12 @@ generate_for_end: |-
end // of for loop with iterator {dimension} end // of for loop with iterator {dimension}
signal_declaration: |- signal_declaration: |-
{type:{signal_width}} {name:{name_width}}{unpacked_dim}; {type:{signal_width}} {name:{name_width}}{unpacked_dim};
sw_read_assignment_var_name:
rtl: |-
{path}_rd_mux_in
signals:
- name: '{path}_rd_mux_in'
signal_type: 'logic [{accesswidth}:0]'
sw_read_assignment:
rtl: |-
assign {sw_read_assignment_var_name}{genvars} = {{{list_of_fields}}};

View File

@ -16,6 +16,9 @@ module_instantiation:
.byte_enable, .byte_enable,
.sw_wr_bus, .sw_wr_bus,
// Inputs from internal logic
.sw_rd_bus,
// Bus protocol // Bus protocol
.HRESETn, .HRESETn,
.HCLK, .HCLK,
@ -42,6 +45,8 @@ module_instantiation:
signal_type: 'logic [ 3:0]' signal_type: 'logic [ 3:0]'
- name: 'sw_wr_bus' - name: 'sw_wr_bus'
signal_type: 'logic [31:0]' signal_type: 'logic [31:0]'
- name: 'sw_rd_bus'
signal_type: 'logic [31:0]'
input_ports: input_ports:
- name: 'HRESETn' - name: 'HRESETn'
signal_type: '' signal_type: ''