From 9385f59ac7c1211fa5709d83828fa5c284d711d8 Mon Sep 17 00:00:00 2001 From: Dennis Date: Sun, 27 Jun 2021 17:04:48 +0200 Subject: [PATCH] Add counters (w/o threshold property and w/o an overflow property) Saturating and non-saturating counters are supported. Furthermore, dynamic and static incrvalues and the incrwidth property is supported. --- srdl2sv/components/field.py | 248 ++++++++++++++++++++++- srdl2sv/components/templates/fields.yaml | 90 ++++++-- 2 files changed, 324 insertions(+), 14 deletions(-) diff --git a/srdl2sv/components/field.py b/srdl2sv/components/field.py index 10e9617..f59d8a3 100644 --- a/srdl2sv/components/field.py +++ b/srdl2sv/components/field.py @@ -1,6 +1,7 @@ import math import importlib.resources as pkg_resources +import sys import yaml from systemrdl.node import FieldNode, SignalNode @@ -47,6 +48,7 @@ class Field(Component): self.__add_hw_access() self.__add_combo() self.__add_swmod_swacc() + self.__add_counter() self.add_sw_access(obj) @@ -206,6 +208,240 @@ class Field(Component): self.access_rtl['sw_read'] = [access_rtl['sw_read']] self.access_rtl['sw_write'] = [access_rtl['sw_write']] + def __add_counter(self): + if self.obj.get_property('counter'): + self.logger.debug("Detected counter property") + + # Determine saturation values + if isinstance(self.obj.get_property('incrsaturate'), bool): + if self.obj.get_property('incrsaturate'): + incr_sat_value = 2**self.obj.width-1 + else: + incr_sat_value = False + else: + incr_sat_value = self.obj.get_property('incrsaturate') + + if isinstance(self.obj.get_property('decrsaturate'), bool): + if self.obj.get_property('decrsaturate'): + decr_sat_value = 2**self.obj.width-1 + else: + decr_sat_value = False + else: + decr_sat_value = self.obj.get_property('decrsaturate') + + # Determine with what value the counter is incremented + # According to the spec, the incrvalue/decrvalue default to '1' + obj_incr_value = self.obj.get_property('incrvalue') + obj_decr_value = self.obj.get_property('decrvalue') + obj_incr_width = self.obj.get_property('incrwidth') + obj_decr_width = self.obj.get_property('decrwidth') + + if obj_incr_value == 0: + incr_value = None + incr_width = 0 + elif obj_incr_value is None: + if obj_incr_width: + # Decrement value is not set. Check if incrwidth is set and use + # that is applicable + incr_value = False + incr_width = obj_incr_width + + # Doesn't return RTL, only adds input port + self.process_yaml( + Field.templ_dict['counter_incr_input'], + {'path': self.path_underscored, + 'genvars': self.genvars_str, + 'incr_width': incr_width-1 + } + ) + else: + # Otherwise, use default value according to LRM + incr_value = '1' + incr_width = 1 + elif isinstance(obj_incr_value, int): + incr_value = str(obj_incr_value) + incr_width = math.floor(math.log2(obj_incr_value)+1) + + if obj_incr_width: + self.logger.error( + "The 'incrwidth' and 'incrvalue' properties are both "\ + "defined. This is not legal and the incrwidth property "\ + "will be ignored!") + else: + incr_value = self.get_signal_name(obj_incr_value) + incr_width = obj_incr_value.width + + if obj_incr_value.width > self.obj.width: + self.logger.error( + "Width of 'incr_value' signal '{}' is wider than current "\ + "counter field. This could potentially cause ugly errors.".format( + obj_incr_value.get_path())) + + if obj_incr_width: + self.logger.error( + "The 'incrwidth' and 'incrvalue' properties are both "\ + "defined. This is not legal and the incrwidth property "\ + "will be ignored!") + + + if incr_value: + self.rtl_footer.append( + self.process_yaml( + Field.templ_dict['counter_internal_incr_signal'], + {'path': self.path_underscored, + 'genvars': self.genvars_str, + 'incr_width': incr_width-1, + 'incr_value': incr_value, + } + ) + ) + + if obj_decr_value == 0: + decr_value = None + decr_width = 0 + elif obj_decr_value is None: + if obj_decr_width: + # Decrement value is not set. Check if decrwidth is set and use + # that is applicable + decr_value = False + decr_width = obj_decr_width + + # Doesn't return RTL, only adds input port + self.process_yaml( + Field.templ_dict['counter_decr_input'], + {'path': self.path_underscored, + 'genvars': self.genvars_str, + 'decr_width': decr_width-1 + } + ) + else: + # Otherwise, use default value according to LRM + decr_value = '1' + decr_width = 1 + elif isinstance(obj_decr_value, int): + decr_value = str(obj_decr_value) + decr_width = math.floor(math.log2(obj_decr_value)+1) + + if obj_decr_width: + self.logger.error( + "The 'decrwidth' and 'decrvalue' properties are both "\ + "defined. This is not legal and the decrwidth property "\ + "will be ignored!") + else: + decr_value = self.get_signal_name(obj_decr_value) + decr_width = obj_decr_value.width + + if obj_decr_value.width > self.obj.width: + self.logger.error( + "Width of 'decr_value' signal '{}' is wider than current "\ + "counter field. This could potentially cause ugly errors.".format( + obj_decr_value.get_path())) + + if obj_decr_width: + self.logger.error( + "The 'decrwidth' and 'decrvalue' properties are both "\ + "defined. This is not legal and the decrwidth property "\ + "will be ignored!") + + + if decr_value: + self.rtl_footer.append( + self.process_yaml( + Field.templ_dict['counter_internal_decr_signal'], + {'path': self.path_underscored, + 'genvars': self.genvars_str, + 'decr_width': decr_width-1, + 'decr_value': decr_value, + } + ) + ) + + if (incr_width or incr_value) and (decr_width or decr_value): + sat_condition = [] + if incr_sat_value: + sat_condition.append( + self.process_yaml( + Field.templ_dict['incr_decr_sat_counter_condition'], + {'path': self.path_underscored, + 'genvars': self.genvars_str, + 'greater_smaller': '>', + 'sat_value': incr_sat_value + } + ) + ) + + if decr_sat_value: + if sat_condition: + sat_condition.append(' && ') + + sat_condition.append( + self.process_yaml( + Field.templ_dict['incr_decr_sat_counter_condition'], + {'path': self.path_underscored, + 'genvars': self.genvars_str, + 'greater_smaller': '<', + 'sat_value': decr_sat_value + } + ) + ) + + counter_logic = self.process_yaml( + Field.templ_dict['incr_decr_counter'], + {'path': self.path_underscored, + 'genvars': self.genvars_str, + 'incr_decr_sat_counter_condition': ''.join(sat_condition), + } + ) + elif incr_width or incr_value: + sat_condition = self.process_yaml( + Field.templ_dict['incr_sat_counter_condition'], + {'path': self.path_underscored, + 'genvars': self.genvars_str, + 'sat_value': incr_sat_value, + } + ) if incr_sat_value else '1' + + counter_logic = self.process_yaml( + Field.templ_dict['incr_counter'], + {'path': self.path_underscored, + 'genvars': self.genvars_str, + 'incr_sat_counter_condition': sat_condition, + } + ) + elif decr_width or decr_value: + sat_condition = self.process_yaml( + Field.templ_dict['decr_sat_counter_condition'], + {'path': self.path_underscored, + 'genvars': self.genvars_str, + 'sat_value': decr_sat_value, + } + ) if decr_sat_value else '1' + + counter_logic = self.process_yaml( + Field.templ_dict['decr_counter'], + {'path': self.path_underscored, + 'genvars': self.genvars_str, + 'decr_sat_counter_condition': sat_condition, + } + ) + else: + self.logger.fatal("Illegal counter configuration! Both 'incr_value' "\ + "and 'decr_value' are forced to 0. If you intended "\ + "to use 'incr_width' or 'decr_width', simply don't "\ + "force 'incr_value' or 'decr_value' to any value.") + sys.exit(1) + + self.rtl_footer.append( + self.process_yaml( + Field.templ_dict['counter'], + {'path': self.path_underscored, + 'genvars': self.genvars_str, + 'counter_logic': counter_logic, + 'field_type': self.field_type, + } + ) + ) + def __add_swmod_swacc(self): if self.obj.get_property('swmod'): self.logger.debug("Field has swmod property") @@ -284,7 +520,17 @@ class Field(Component): def __add_hw_access(self): # Define hardware access (if applicable) - if self.obj.get_property('hw') in (AccessType.rw, AccessType.w): + if self.obj.get_property('counter'): + self.access_rtl['hw_write'] = ([ + self.process_yaml( + Field.templ_dict['hw_access_counter'], + {'path': self.path_underscored, + 'genvars': self.genvars_str, + 'field_type': self.field_type} + ) + ], + False) + elif self.obj.get_property('hw') in (AccessType.rw, AccessType.w): write_condition = 'hw_access_we_wel' if self.we_or_wel else 'hw_access_no_we_wel' # if-line of hw-access diff --git a/srdl2sv/components/templates/fields.yaml b/srdl2sv/components/templates/fields.yaml index 8b20ee7..68be6ca 100644 --- a/srdl2sv/components/templates/fields.yaml +++ b/srdl2sv/components/templates/fields.yaml @@ -56,6 +56,17 @@ hw_access_field: input_ports: - name: '{path}_in' signal_type: '{field_type}' +hw_access_counter: + rtl: |- + if ({path}_update_cnt{genvars}) + begin + {path}_q{genvars} <= {path}_next{genvars}; + end + signals: + - name: '{path}_update_cnt' + signal_type: 'logic' + - name: '{path}_next' + signal_type: '{field_type}' end_field_ff: rtl: |- end // of {path}'s always_ff @@ -181,38 +192,91 @@ swmod_assign: signal_type: 'logic' counter: rtl: |- + + // Combinational logic that implements counter always_comb begin {path}_next{genvars} = {path}_q{genvars}; {path}_update_cnt{genvars} = 0; - {incr_counter} + {counter_logic} - {decr_counter} end + signals: + - name: '{path}_update_cnt' + signal_type: 'logic' + - name: '{path}_next' + signal_type: '{field_type}' +counter_internal_incr_signal: + rtl: |- + assign {path}_incr_val{genvars} = {incr_value}; + signals: + - name: '{path}_incr_val' + signal_type: 'logic [{incr_width}:0]' +counter_internal_decr_signal: + rtl: |- + assign {path}_decr_val{genvars} = {decr_value}; + signals: + - name: '{path}_decr_val' + signal_type: 'logic [{decr_width}:0]' +counter_incr_input: + rtl: '' + input_ports: + - name: '{path}_incr_val' + signal_type: 'logic [{incr_width}:0]' +counter_decr_input: + rtl: '' + input_ports: + - name: '{path}_decr_val' + signal_type: 'logic [{decr_width}:0]' incr_counter: rtl: |- - {incr_counter_condition} + if ({path}_incr{genvars}) begin - {path}_next{genvars} += {path}_{operation_str}_val{genvars}; + if ({incr_sat_counter_condition}) + begin + {path}_next{genvars} += {path}_incr_val{genvars}; {path}_update_cnt{genvars} = 1; end + end + input_ports: + - name: '{path}_incr' + signal_type: 'logic' decr_counter: rtl: |- - {incr_counter_condition} + if ({path}_decr{genvars}) begin - {path}_next{genvars} += {path}_{operation_str}_val{genvars}; + if ({decr_sat_counter_condition}) + begin + {path}_next{genvars} -= {path}_decr_val{genvars}; {path}_update_cnt{genvars} = 1; end -incr_counter_condition: + end + input_ports: + - name: '{path}_decr' + signal_type: 'logic' +incr_decr_counter: rtl: |- - if ({path}_incr{genvars}) + if ({path}_incr{genvars} || {path}_decr{genvars}) + begin + if ({incr_decr_sat_counter_condition}) + begin + {path}_next{genvars} += {path}_incr_val{genvars}; + {path}_next{genvars} -= {path}_decr_val{genvars}; + {path}_update_cnt{genvars} = 1; + end + end + input_ports: + - name: '{path}_incr' + signal_type: 'logic' + - name: '{path}_decr' + signal_type: 'logic' incr_sat_counter_condition: rtl: |- - if ({path}_incr{genvars} && {path}_next{genvars} + {path}_incr_val{genvars} <= {sat_value}) -decr_counter_condition: - rtl: |- - if ({path}_decr{genvars}) + {path}_next{genvars} + {path}_incr_val{genvars} <= {sat_value} decr_sat_counter_condition: rtl: |- - if ({path}_decr{genvars} && {path}_next{genvars} - {path}_decr_val{genvars} >= {sat_value}) + {path}_next{genvars} - {path}_decr_val{genvars} >= {sat_value} +incr_decr_sat_counter_condition: + rtl: |- + ({path}_next{genvars} + {path}_incr_val{genvars} - {path}_decr_val{genvars} {greater_smaller}= {sat_value})