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")
|
||||
|
||||
# Create RTL of all registers
|
||||
[x.create_rtl() for x in self.registers.values()]
|
||||
|
||||
# Add bus widget ports
|
||||
widget_rtl = self.__get_widget_ports_rtl()
|
||||
|
||||
@ -159,9 +162,32 @@ class AddrMap(Component):
|
||||
# Append genvars
|
||||
self.__append_genvars()
|
||||
|
||||
# Create read multiplexer
|
||||
self.__create_mux_string()
|
||||
|
||||
# Add endmodule keyword
|
||||
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):
|
||||
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)
|
||||
|
@ -26,6 +26,7 @@ class Component():
|
||||
self.field_type = ''
|
||||
|
||||
# Save object
|
||||
# TODO: should probably be list because of alias registers
|
||||
self.obj = obj
|
||||
|
||||
# Save name
|
||||
@ -100,14 +101,6 @@ class Component():
|
||||
rtl_children = []
|
||||
|
||||
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())
|
||||
|
||||
# Concatenate header, main, and footer
|
||||
|
@ -155,10 +155,14 @@ class Field(Component):
|
||||
onread = obj.get_property('onread')
|
||||
|
||||
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:
|
||||
self.logger.warning("The OnReadType.ruser is not yet supported!")
|
||||
else:
|
||||
self.logger.error("The OnReadType.ruser is not yet supported!")
|
||||
elif onread:
|
||||
access_rtl['sw_read'][0].append(
|
||||
self.process_yaml(
|
||||
Field.templ_dict[str(onread)],
|
||||
@ -412,6 +416,11 @@ class Field(Component):
|
||||
# Save byte boundaries
|
||||
self.lsbyte = math.floor(obj.inst.lsb / 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,
|
||||
# the reset value, and whether the field actually has a reset
|
||||
|
@ -122,6 +122,9 @@ class RegFile(Component):
|
||||
glbl_settings['generate_active'] = False
|
||||
self.rtl_footer.append("endgenerate")
|
||||
|
||||
# Create RTL of all registers
|
||||
[x.create_rtl() for x in self.registers.values()]
|
||||
|
||||
def __process_variables(self,
|
||||
obj: node.RegfileNode,
|
||||
parents_dimensions: list,
|
||||
@ -160,4 +163,7 @@ class RegFile(Component):
|
||||
genvars = ['[{}]'.format(chr(97+i)) for i in range(self.dimensions)]
|
||||
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 sys
|
||||
import yaml
|
||||
import itertools
|
||||
|
||||
from systemrdl import node
|
||||
|
||||
@ -28,8 +29,7 @@ class Register(Component):
|
||||
# Save and/or process important variables
|
||||
self.__process_variables(obj, parents_dimensions, parents_stride, glbl_settings)
|
||||
|
||||
# Create RTL for fields
|
||||
# Fields should be in order in RTL, therefore, use list
|
||||
# Create RTL for fields of initial, non-alias register
|
||||
for field in obj.fields():
|
||||
# Use range to save field in an array. Reason is, names are allowed to
|
||||
# change when using an alias
|
||||
@ -44,6 +44,9 @@ class Register(Component):
|
||||
self.children[field_range].sanity_checks()
|
||||
|
||||
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
|
||||
if self.dimensions and not self.generate_active:
|
||||
self.rtl_header.append("generate")
|
||||
@ -69,15 +72,89 @@ class Register(Component):
|
||||
if self.dimensions and not self.generate_active:
|
||||
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
|
||||
self.rtl_header = [
|
||||
Register.templ_dict['reg_comment'].format(
|
||||
name = obj.inst_name,
|
||||
name = self.obj.inst_name,
|
||||
dimensions = self.dimensions,
|
||||
depth = self.depth),
|
||||
*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):
|
||||
# Assign variables from bus
|
||||
self.obj.current_idx = [0]
|
||||
@ -98,8 +175,9 @@ class Register(Component):
|
||||
'depth': self.depth,
|
||||
'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
|
||||
dict_list = [(key, value) for (key, value) in self.get_signals().items()]
|
||||
|
||||
@ -143,7 +221,8 @@ class Register(Component):
|
||||
|
||||
# Add name to list
|
||||
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(
|
||||
self,
|
||||
@ -151,13 +230,15 @@ class Register(Component):
|
||||
parents_dimensions: list,
|
||||
parents_stride: list,
|
||||
glbl_settings: dict):
|
||||
# Save object
|
||||
self.obj = obj
|
||||
|
||||
# Save name
|
||||
self.obj.current_idx = [0]
|
||||
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
|
||||
self.create_underscored_path()
|
||||
@ -165,6 +246,9 @@ class Register(Component):
|
||||
# Gnerate already started?
|
||||
self.generate_active = glbl_settings['generate_active']
|
||||
|
||||
# Empty array for mux-input signals
|
||||
self.sw_read_assignment_var_name = []
|
||||
|
||||
# Determine dimensions of register
|
||||
if obj.is_array:
|
||||
self.sel_arr = 'array'
|
||||
@ -183,7 +267,7 @@ class Register(Component):
|
||||
self.sel_arr = 'single'
|
||||
self.total_array_dimensions = parents_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?
|
||||
self.parents_depths = len(parents_dimensions)
|
||||
@ -200,6 +284,7 @@ class Register(Component):
|
||||
|
||||
# Determine value to compare address with
|
||||
genvars_sum = []
|
||||
genvars_sum_vectorized = []
|
||||
try:
|
||||
for i, stride in enumerate(self.total_stride):
|
||||
genvars_sum.append(chr(97+i))
|
||||
@ -207,7 +292,14 @@ class Register(Component):
|
||||
genvars_sum.append(str(stride))
|
||||
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_vectorized.pop()
|
||||
|
||||
self.logger.debug(
|
||||
"Multidimensional with dimensions '{}' and stride '{}'".format(
|
||||
@ -221,4 +313,5 @@ class Register(Component):
|
||||
"Caugt expected IndexError because genvars_sum is empty")
|
||||
|
||||
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:
|
||||
rtl: |-
|
||||
{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:
|
||||
output_ports:
|
||||
rw_wire_assign_multi_dim:
|
||||
rtl: |
|
||||
rtl: |-
|
||||
// Register-activation for '{path}' {alias}
|
||||
assign {path}_accss{genvars} = addr == {addr}+({genvars_sum});
|
||||
assign {path}_sw_wr{genvars} = {path}_accss{genvars} && r_vld;
|
||||
@ -45,3 +45,12 @@ generate_for_end: |-
|
||||
end // of for loop with iterator {dimension}
|
||||
signal_declaration: |-
|
||||
{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,
|
||||
.byte_enable,
|
||||
.sw_wr_bus,
|
||||
|
||||
// Inputs from internal logic
|
||||
.sw_rd_bus,
|
||||
|
||||
// Bus protocol
|
||||
.HRESETn,
|
||||
@ -42,6 +45,8 @@ module_instantiation:
|
||||
signal_type: 'logic [ 3:0]'
|
||||
- name: 'sw_wr_bus'
|
||||
signal_type: 'logic [31:0]'
|
||||
- name: 'sw_rd_bus'
|
||||
signal_type: 'logic [31:0]'
|
||||
input_ports:
|
||||
- name: 'HRESETn'
|
||||
signal_type: ''
|
||||
|
Loading…
Reference in New Issue
Block a user