Improve counter property

The counter now covers more corner cases regarding saturation.
Furthermore, a bug that caused incr & decr-counters to always
inadvertently increment and decrement, even if only one of the two
signals was set.

Furthermore, the overflow signal is now generated in RTL.

Still missing:
    - incrthreshold/decrthreshold is not yet supported
    - It is not yet supported to assign an underflow/overflow to the
      input of another counter.
This commit is contained in:
Dennis Potter 2021-06-28 23:58:45 +02:00
parent 4144329f3f
commit 18204d9a3e
Signed by: Dennis
GPG Key ID: 186A8AD440942BAF
2 changed files with 294 additions and 137 deletions

View File

@ -212,22 +212,30 @@ class Field(Component):
if self.obj.get_property('counter'): if self.obj.get_property('counter'):
self.logger.debug("Detected counter property") self.logger.debug("Detected counter property")
# Determine saturation values self.rtl_footer.append(Field.templ_dict['counter_comment']['rtl'])
# Determine saturation values and add appropriate RTL
if isinstance(self.obj.get_property('incrsaturate'), bool): if isinstance(self.obj.get_property('incrsaturate'), bool):
if self.obj.get_property('incrsaturate'): if self.obj.get_property('incrsaturate'):
incr_sat_value = 2**self.obj.width-1 incr_sat_value = 2**self.obj.width-1
overflow_value = incr_sat_value
else: else:
incr_sat_value = False incr_sat_value = False
overflow_value = 2**self.obj.width-1
else: else:
incr_sat_value = self.obj.get_property('incrsaturate') incr_sat_value = self.obj.get_property('incrsaturate')
overflow_value = incr_sat_value
if isinstance(self.obj.get_property('decrsaturate'), bool): if isinstance(self.obj.get_property('decrsaturate'), bool):
if self.obj.get_property('decrsaturate'): if self.obj.get_property('decrsaturate'):
decr_sat_value = 2**self.obj.width-1 decr_sat_value = 0
underflow_value = decr_sat_value
else: else:
decr_sat_value = False decr_sat_value = False
underflow_value = 0
else: else:
decr_sat_value = self.obj.get_property('decrsaturate') decr_sat_value = self.obj.get_property('decrsaturate')
underflow_value = decr_sat_value
# Determine with what value the counter is incremented # Determine with what value the counter is incremented
# According to the spec, the incrvalue/decrvalue default to '1' # According to the spec, the incrvalue/decrvalue default to '1'
@ -236,19 +244,23 @@ class Field(Component):
obj_incr_width = self.obj.get_property('incrwidth') obj_incr_width = self.obj.get_property('incrwidth')
obj_decr_width = self.obj.get_property('decrwidth') obj_decr_width = self.obj.get_property('decrwidth')
incr_width_input = False
if obj_incr_value == 0: if obj_incr_value == 0:
incr_value = None incr_value = 0
incr_width = 0 incr_width = 1
elif obj_incr_value is None: elif obj_incr_value is None:
# Increment value is not set. Check if incrwidth is set and use
# that is applicable
if obj_incr_width: if obj_incr_width:
# Decrement value is not set. Check if incrwidth is set and use
# that is applicable
incr_value = False incr_value = False
incr_width = obj_incr_width incr_width = obj_incr_width
incr_width_input = True
# Doesn't return RTL, only adds input port # Doesn't return RTL, only adds input port
self.process_yaml( self.process_yaml(
Field.templ_dict['counter_incr_input'], Field.templ_dict['counter_incr_val_input'],
{'path': self.path_underscored, {'path': self.path_underscored,
'genvars': self.genvars_str, 'genvars': self.genvars_str,
'incr_width': incr_width-1 'incr_width': incr_width-1
@ -259,6 +271,7 @@ class Field(Component):
incr_value = '1' incr_value = '1'
incr_width = 1 incr_width = 1
elif isinstance(obj_incr_value, int): elif isinstance(obj_incr_value, int):
# An explicit width is set for the incr_val
incr_value = str(obj_incr_value) incr_value = str(obj_incr_value)
incr_width = math.floor(math.log2(obj_incr_value)+1) incr_width = math.floor(math.log2(obj_incr_value)+1)
@ -284,10 +297,12 @@ class Field(Component):
"will be ignored!") "will be ignored!")
if incr_value: # If no input is defined for the increment value, define
# an internal signal. It is possible that this is tied to 0.
if not incr_width_input:
self.rtl_footer.append( self.rtl_footer.append(
self.process_yaml( self.process_yaml(
Field.templ_dict['counter_internal_incr_signal'], Field.templ_dict['counter_internal_incr_val_signal'],
{'path': self.path_underscored, {'path': self.path_underscored,
'genvars': self.genvars_str, 'genvars': self.genvars_str,
'incr_width': incr_width-1, 'incr_width': incr_width-1,
@ -296,19 +311,24 @@ class Field(Component):
) )
) )
# Handle decrement value
decr_width_input = False
if obj_decr_value == 0: if obj_decr_value == 0:
decr_value = None decr_value = 0
decr_width = 0 decr_width = 1
elif obj_decr_value is None: elif obj_incr_value is None:
# Decrement value is not set. Check if decrwidth is set and use
# that is applicable
if obj_decr_width: if obj_decr_width:
# Decrement value is not set. Check if decrwidth is set and use
# that is applicable
decr_value = False decr_value = False
decr_width = obj_decr_width decr_width = obj_decr_width
decr_width_input = True
# Doesn't return RTL, only adds input port # Doesn't return RTL, only adds input port
self.process_yaml( self.process_yaml(
Field.templ_dict['counter_decr_input'], Field.templ_dict['counter_decr_val_input'],
{'path': self.path_underscored, {'path': self.path_underscored,
'genvars': self.genvars_str, 'genvars': self.genvars_str,
'decr_width': decr_width-1 'decr_width': decr_width-1
@ -319,6 +339,7 @@ class Field(Component):
decr_value = '1' decr_value = '1'
decr_width = 1 decr_width = 1
elif isinstance(obj_decr_value, int): elif isinstance(obj_decr_value, int):
# An explicit width is set for the decr_val
decr_value = str(obj_decr_value) decr_value = str(obj_decr_value)
decr_width = math.floor(math.log2(obj_decr_value)+1) decr_width = math.floor(math.log2(obj_decr_value)+1)
@ -344,10 +365,12 @@ class Field(Component):
"will be ignored!") "will be ignored!")
if decr_value: # If no input is defined for the decrement value, define
# an internal signal. It is possible that this is tied to 0.
if not decr_width_input:
self.rtl_footer.append( self.rtl_footer.append(
self.process_yaml( self.process_yaml(
Field.templ_dict['counter_internal_decr_signal'], Field.templ_dict['counter_internal_decr_val_signal'],
{'path': self.path_underscored, {'path': self.path_underscored,
'genvars': self.genvars_str, 'genvars': self.genvars_str,
'decr_width': decr_width-1, 'decr_width': decr_width-1,
@ -356,92 +379,174 @@ class Field(Component):
) )
) )
if (incr_width or incr_value) and (decr_width or decr_value): # Handle the increment/decrement signals.
sat_condition = [] # If the increment or decrement signal is not set, use an input
if incr_sat_value: # if the decrement value is bigger than 0
sat_condition.append( if not incr_value and not decr_value:
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' "\ self.logger.fatal("Illegal counter configuration! Both 'incr_value' "\
"and 'decr_value' are forced to 0. If you intended "\ "and 'decr_value' are forced to 0. If you intended "\
"to use 'incr_width' or 'decr_width', simply don't "\ "to use 'incr_width' or 'decr_width', simply don't "\
"force 'incr_value' or 'decr_value' to any value.") "force 'incr_value' or 'decr_value' to any value.")
sys.exit(1) sys.exit(1)
if incr_value:
incr = self.obj.get_property('incr')
if not incr:
# Will only add input port but not return any RTL
self.process_yaml(
Field.templ_dict['counter_incr_input'],
{'path': self.path_underscored}
)
else:
self.rtl_footer.append(
self.process_yaml(
Field.templ_dict['counter_internal_incr_signal'],
{'path': self.path_underscored,
'genvars': self.genvars_str,
'incr': self.get_signal_name(incr)
}
)
)
if incr.width > 0:
self.logger.error("Increment signal '{}' is wider than 1-bit. "\
"This might result in unwanted behavior and "\
"will also cause Lint-errors.".format(
incr.inst_name))
else:
# Tie signal to 0
self.rtl_footer.append(
self.process_yaml(
Field.templ_dict['counter_internal_incr_signal'],
{'path': self.path_underscored,
'genvars': self.genvars_str,
'incr': '0'
}
)
)
if decr_value:
decr = self.obj.get_property('decr')
if not self.obj.get_property('decr'):
# Will only add input port but not return any RTL
self.process_yaml(
Field.templ_dict['counter_decr_input'],
{'path': self.path_underscored}
)
else:
self.rtl_footer.append(
self.process_yaml(
Field.templ_dict['counter_internal_decr_signal'],
{'path': self.path_underscored,
'genvars': self.genvars_str,
'decr': self.get_signal_name(decr)
}
)
)
if decr.width > 0:
self.logger.error("Decrement signal '{}' is wider than 1-bit. "\
"This might result in unwanted behavior and "\
"will also cause Lint-errors.".format(
decr.inst_name))
else:
# Tie signal to 0
self.rtl_footer.append(
self.process_yaml(
Field.templ_dict['counter_internal_decr_signal'],
{'path': self.path_underscored,
'genvars': self.genvars_str,
'decr': '0'
}
)
)
# Handle saturation signals
if not incr_sat_value:
self.rtl_footer.append(
self.process_yaml(
Field.templ_dict['counter_incr_sat_tied'],
{'path': self.path_underscored,
'genvars': self.genvars_str,
}
)
)
else:
self.rtl_footer.append(
self.process_yaml(
Field.templ_dict['counter_incr_sat'],
{'path': self.path_underscored,
'genvars': self.genvars_str,
'incr_width': incr_width,
'decr_width': decr_width,
'sat_value': incr_sat_value,
}
)
)
if not decr_sat_value:
self.rtl_footer.append(
self.process_yaml(
Field.templ_dict['counter_decr_sat_tied'],
{'path': self.path_underscored,
'genvars': self.genvars_str,
}
)
)
else:
self.rtl_footer.append(
self.process_yaml(
Field.templ_dict['counter_decr_sat'],
{'path': self.path_underscored,
'genvars': self.genvars_str,
'incr_width': incr_width,
'decr_width': decr_width,
'sat_value': decr_sat_value,
}
)
)
# Handle overflow & underflow signals
if self.obj.get_property('overflow'):
self.rtl_footer.append(
self.process_yaml(
Field.templ_dict['counter_overflow'],
{'path': self.path_underscored,
'genvars': self.genvars_str,
'incr_width': incr_width,
'decr_width': decr_width,
'overflow_value': overflow_value,
}
)
)
if self.obj.get_property('underflow'):
self.rtl_footer.append(
self.process_yaml(
Field.templ_dict['counter_underflow'],
{'path': self.path_underscored,
'genvars': self.genvars_str,
'incr_width': incr_width,
'decr_width': decr_width,
'underflow_value': underflow_value,
}
)
)
# Implement actual counter logic
self.rtl_footer.append( self.rtl_footer.append(
self.process_yaml( self.process_yaml(
Field.templ_dict['counter'], Field.templ_dict['counter'],
{'path': self.path_underscored, {'path': self.path_underscored,
'genvars': self.genvars_str, 'genvars': self.genvars_str,
'counter_logic': counter_logic,
'field_type': self.field_type,
} }
) )
) )
def __add_swmod_swacc(self): def __add_swmod_swacc(self):
if self.obj.get_property('swmod'): if self.obj.get_property('swmod'):
self.logger.debug("Field has swmod property") self.logger.debug("Field has swmod property")

View File

@ -92,7 +92,7 @@ hw_access_field:
signal_type: '{field_type}' signal_type: '{field_type}'
hw_access_counter: hw_access_counter:
rtl: |- rtl: |-
if ({path}_update_cnt{genvars}) if ({path}_incr{genvars} || {path}_decr{genvars})
<<INDENT>> <<INDENT>>
{enable_mask_start} {enable_mask_start}
{path}_q{genvars}{idx} <= {path}_next{genvars}{idx}; {path}_q{genvars}{idx} <= {path}_next{genvars}{idx};
@ -226,93 +226,145 @@ swmod_assign:
output_ports: output_ports:
- name: '{path}_swmod' - name: '{path}_swmod'
signal_type: 'logic' signal_type: 'logic'
counter_comment:
rtl: |-
/***********************
* Counter Combo Logic *
***********************/
counter: counter:
rtl: |- rtl: |-
// Combinational logic that implements counter // Combinational logic that implements counter
// Note that the three branches for all three possibilities
// are implemented but that either the _incr or the _decr value
// might be tied to 0.
always_comb always_comb
begin begin
{path}_next{genvars} = {path}_q{genvars}; {path}_next{genvars} = {path}_q{genvars};
{path}_update_cnt{genvars} = 0;
{counter_logic}
if ({path}_incr{genvars} && {path}_decr{genvars})
begin
if (!{path}_incr_sat{genvars} && !{path}_decr_sat{genvars})
begin
{path}_next{genvars} += {path}_incr_val{genvars};
{path}_next{genvars} -= {path}_decr_val{genvars};
end
end
else if ({path}_incr{genvars})
begin
if (!{path}_incr_sat{genvars})
<<INDENT>>
{path}_next{genvars} += {path}_incr_val{genvars};
<<UNINDENT>>
end
else if ({path}_decr{genvars})
begin
if (!{path}_decr_sat{genvars})
<<INDENT>>
{path}_next{genvars} += {path}_decr_val{genvars};
<<UNINDENT>>
end
end end
signals: signals:
- name: '{path}_update_cnt'
signal_type: 'logic'
- name: '{path}_next' - name: '{path}_next'
signal_type: '{field_type}' signal_type: '{field_type}'
counter_internal_incr_signal: counter_internal_incr_val_signal:
rtl: |- rtl: |-
assign {path}_incr_val{genvars} = {incr_value}; assign {path}_incr_val{genvars} = {incr_value};
signals: signals:
- name: '{path}_incr_val' - name: '{path}_incr_val'
signal_type: 'logic [{incr_width}:0]' signal_type: 'logic [{incr_width}:0]'
counter_internal_decr_signal: counter_internal_decr_val_signal:
rtl: |- rtl: |-
assign {path}_decr_val{genvars} = {decr_value}; assign {path}_decr_val{genvars} = {decr_value};
signals: signals:
- name: '{path}_decr_val' - name: '{path}_decr_val'
signal_type: 'logic [{decr_width}:0]' signal_type: 'logic [{decr_width}:0]'
counter_incr_input: counter_incr_val_input:
rtl: '' rtl: ''
input_ports: input_ports:
- name: '{path}_incr_val' - name: '{path}_incr_val'
signal_type: 'logic [{incr_width}:0]' signal_type: 'logic [{incr_width}:0]'
counter_decr_input: counter_decr_val_input:
rtl: '' rtl: ''
input_ports: input_ports:
- name: '{path}_decr_val' - name: '{path}_decr_val'
signal_type: 'logic [{decr_width}:0]' signal_type: 'logic [{decr_width}:0]'
incr_counter: counter_internal_incr_signal:
rtl: |- rtl: |-
if ({path}_incr{genvars}) assign {path}_incr{genvars} = {incr};
begin signals:
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' - name: '{path}_incr'
signal_type: 'logic' signal_type: 'logic'
decr_counter: counter_internal_decr_signal:
rtl: |- rtl: |-
if ({path}_decr{genvars}) assign {path}_decr{genvars} = {decr};
begin signals:
if ({decr_sat_counter_condition})
begin
{path}_next{genvars} -= {path}_decr_val{genvars};
{path}_update_cnt{genvars} = 1;
end
end
input_ports:
- name: '{path}_decr' - name: '{path}_decr'
signal_type: 'logic' signal_type: 'logic'
incr_decr_counter: counter_incr_input:
rtl: |- rtl: ''
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: input_ports:
- name: '{path}_incr' - name: '{path}_incr'
signal_type: 'logic' signal_type: ''
counter_decr_input:
rtl: ''
input_ports:
- name: '{path}_decr' - name: '{path}_decr'
signal_type: ''
counter_incr_sat:
rtl: |-
// Determine whether the counter is saturated
// The signal is tied if the counter is not saturating
// in the respective direction
assign {path}_incr_sat{genvars} = {path}_q{genvars} + ({{{incr_width}{{{path}_incr}}}} & {path}_incr_val) - ({{{decr_width}{{{path}_decr}}}} & {path}_decr_val) > {sat_value};
signals:
- name: '{path}_incr_sat'
signal_type: 'logic' signal_type: 'logic'
incr_sat_counter_condition: counter_incr_sat_tied:
rtl: |- rtl: |-
{path}_next{genvars} + {path}_incr_val{genvars} <= {sat_value}
decr_sat_counter_condition: // Determine whether the counter is saturated
// The signal is tied if the counter is not saturating
// in the respective direction
assign {path}_incr_sat{genvars} = 1'b0;
signals:
- name: '{path}_incr_sat'
signal_type: 'logic'
counter_decr_sat:
rtl: |- rtl: |-
{path}_next{genvars} - {path}_decr_val{genvars} >= {sat_value} assign {path}_decr_sat{genvars} = {path}_q{genvars} + ({{{incr_width}{{{path}_incr}}}} & {path}_incr_val) - ({{{decr_width}{{{path}_decr}}}} & {path}_decr_val) > {sat_value};
incr_decr_sat_counter_condition: signals:
- name: '{path}_decr_sat'
signal_type: 'logic'
counter_decr_sat_tied:
rtl: |- rtl: |-
({path}_next{genvars} + {path}_incr_val{genvars} - {path}_decr_val{genvars} {greater_smaller}= {sat_value}) assign {path}_decr_sat{genvars} = 1'b0;
signals:
- name: '{path}_decr_sat'
signal_type: 'logic'
counter_overflow:
rtl: |-
// Logic to determine occurance of an overflow
assign {path}_overflow_int{genvars} = {path}_q{genvars} + ({{{incr_width}{{{path}_incr}}}} & {path}_incr_val) - ({{{decr_width}{{{path}_decr}}}} & {path}_decr_val) > {overflow_value};
assign {path}_overflow{genvars} = {path}_incr{genvars} && {path}_overflow_int{genvars};
signals:
- name: '{path}_overflow_int'
signal_type: 'logic'
output_ports:
- name: '{path}_overflow'
signal_type: 'logic'
counter_underflow:
rtl: |-
// Logic to determine occurance of an underflow
assign {path}_underflow_int{genvars} = {path}_q{genvars} + ({{{incr_width}{{{path}_incr}}}} & {path}_incr_val) - ({{{decr_width}{{{path}_decr}}}} & {path}_decr_val) > {underflow_value};
assign {path}_underflow{genvars} = {path}_incr{genvars} && {path}_underflow_int{genvars};
signals:
- name: '{path}_underflow_int'
signal_type: 'logic'
output_ports:
- name: '{path}_underflow'
signal_type: 'logic'