mirror of
https://github.com/Silicon1602/srdl2sv.git
synced 2024-12-22 06:58:41 +00:00
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:
parent
b2c756af41
commit
32c6fc3c4a
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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()
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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 = {};
|
||||||
|
@ -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}}};
|
||||||
|
@ -15,6 +15,9 @@ module_instantiation:
|
|||||||
.r_vld,
|
.r_vld,
|
||||||
.byte_enable,
|
.byte_enable,
|
||||||
.sw_wr_bus,
|
.sw_wr_bus,
|
||||||
|
|
||||||
|
// Inputs from internal logic
|
||||||
|
.sw_rd_bus,
|
||||||
|
|
||||||
// Bus protocol
|
// Bus protocol
|
||||||
.HRESETn,
|
.HRESETn,
|
||||||
@ -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: ''
|
||||||
|
Loading…
Reference in New Issue
Block a user