From 32c6fc3c4a5bcb332758eeb6d8a9e9fa4bb1f8f0 Mon Sep 17 00:00:00 2001 From: Dennis Date: Wed, 23 Jun 2021 01:03:11 +0200 Subject: [PATCH] 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. --- srdl2sv/components/addrmap.py | 26 +++++ srdl2sv/components/component.py | 9 +- srdl2sv/components/field.py | 15 ++- srdl2sv/components/regfile.py | 6 + srdl2sv/components/register.py | 111 +++++++++++++++++-- srdl2sv/components/templates/addrmap.yaml | 13 +++ srdl2sv/components/templates/regs.yaml | 11 +- srdl2sv/components/widgets/amba3ahblite.yaml | 5 + 8 files changed, 175 insertions(+), 21 deletions(-) diff --git a/srdl2sv/components/addrmap.py b/srdl2sv/components/addrmap.py index 97f7afa..d8275d9 100644 --- a/srdl2sv/components/addrmap.py +++ b/srdl2sv/components/addrmap.py @@ -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) diff --git a/srdl2sv/components/component.py b/srdl2sv/components/component.py index 6a8bfd2..67776eb 100644 --- a/srdl2sv/components/component.py +++ b/srdl2sv/components/component.py @@ -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 diff --git a/srdl2sv/components/field.py b/srdl2sv/components/field.py index 8522a1d..310041e 100644 --- a/srdl2sv/components/field.py +++ b/srdl2sv/components/field.py @@ -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 diff --git a/srdl2sv/components/regfile.py b/srdl2sv/components/regfile.py index eb6f4d4..23abc7f 100644 --- a/srdl2sv/components/regfile.py +++ b/srdl2sv/components/regfile.py @@ -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() diff --git a/srdl2sv/components/register.py b/srdl2sv/components/register.py index 02db5cd..eef1d57 100644 --- a/srdl2sv/components/register.py +++ b/srdl2sv/components/register.py @@ -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) diff --git a/srdl2sv/components/templates/addrmap.yaml b/srdl2sv/components/templates/addrmap.yaml index f3c8fba..95f578c 100644 --- a/srdl2sv/components/templates/addrmap.yaml +++ b/srdl2sv/components/templates/addrmap.yaml @@ -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 = {}; diff --git a/srdl2sv/components/templates/regs.yaml b/srdl2sv/components/templates/regs.yaml index 403102e..e85eedd 100644 --- a/srdl2sv/components/templates/regs.yaml +++ b/srdl2sv/components/templates/regs.yaml @@ -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}}}; diff --git a/srdl2sv/components/widgets/amba3ahblite.yaml b/srdl2sv/components/widgets/amba3ahblite.yaml index 396b647..8c55c0c 100644 --- a/srdl2sv/components/widgets/amba3ahblite.yaml +++ b/srdl2sv/components/widgets/amba3ahblite.yaml @@ -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: ''