Add counter support

This commit is contained in:
Alex Mykyta
2021-12-11 20:41:49 -08:00
parent f5b12253ad
commit 9eddc9b60f
40 changed files with 1133 additions and 349 deletions

View File

@@ -60,8 +60,12 @@ class Dereferencer:
return self.get_value(reset_value)
else:
# No reset value defined!
# Callers shall ensure this is impossible
raise RuntimeError
obj.env.msg.warning(
"Field '%s' is a constant but does not have a known value (missing reset). Assigning it a value of X."
% obj.inst_name,
obj.inst.inst_src_ref
)
return "'X"
if isinstance(obj, SignalNode):
# Signals are always inputs from the hwif
@@ -106,28 +110,12 @@ class Dereferencer:
}:
return self.get_value(field.get_property(prop_name))
# Counter properties
if prop_name == 'incr':
prop_value = field.get_property(prop_name)
if prop_value is None:
# unset by the user, points to the implied internal signal
return self.field_logic.get_counter_incr_identifier(field)
else:
return self.get_value(prop_value)
elif prop_name == 'decr':
prop_value = field.get_property(prop_name)
if prop_value is None:
# unset by the user, points to the implied internal signal
return self.field_logic.get_counter_decr_identifier(field)
else:
return self.get_value(prop_value)
# Field Next
if prop_name == "next":
prop_value = field.get_property(prop_name)
if prop_value is None:
# unset by the user, points to the implied internal signal
return self.field_logic.get_field_next_identifier(field)
return self.field_logic.get_field_combo_identifier(field, "next")
else:
return self.get_value(prop_value)
@@ -175,19 +163,31 @@ class Dereferencer:
if prop_name == "swmod":
return self.field_logic.get_swmod_identifier(field)
raise RuntimeError("Unhandled reference to: %s->%s" % (field, prop_name))
"""
TODO:
Resolves to an internal signal used in the field's logic
decrsaturate
decrthreshold
incrsaturate
incrthreshold
overflow
saturate
threshold
"""
# translate aliases
aliases = {
"saturate": "incrsaturate",
"threshold": "incrthreshold",
}
prop_name = aliases.get(prop_name, prop_name)
# Counter properties
if prop_name == 'incr':
return self.field_logic.get_counter_incr_strobe(field)
if prop_name == 'decr':
return self.field_logic.get_counter_decr_strobe(field)
if prop_name in {
'decrsaturate',
'decrthreshold',
'incrsaturate',
'incrthreshold',
'overflow',
'underflow',
}:
return self.field_logic.get_field_combo_identifier(field, prop_name)
raise RuntimeError("Unhandled reference to: %s->%s" % (field, prop_name))
def get_reg_propref_value(self, reg: RegNode, prop_name: str) -> str:

View File

@@ -1,6 +1,7 @@
from typing import TYPE_CHECKING
from systemrdl.rdltypes import PropertyReference, PrecedenceType
from systemrdl.node import Node
from .bases import AssignmentPrecedence, NextStateConditional
from . import sw_onread
@@ -61,41 +62,105 @@ class FieldLogic:
#---------------------------------------------------------------------------
# Field utility functions
#---------------------------------------------------------------------------
def get_storage_identifier(self, node: 'FieldNode') -> str:
def get_storage_identifier(self, field: 'FieldNode') -> str:
"""
Returns the Verilog string that represents the storage register element
for the referenced field
"""
assert node.implements_storage
path = get_indexed_path(self.top_node, node)
assert field.implements_storage
path = get_indexed_path(self.top_node, field)
return f"field_storage.{path}"
def get_field_next_identifier(self, node: 'FieldNode') -> str:
def get_field_combo_identifier(self, field: 'FieldNode', name: str) -> str:
"""
Returns a Verilog string that represents the field's next-state.
This is specifically for use in Field->next property references.
Returns a Verilog string that represents a field's internal combinational
signal.
"""
assert node.implements_storage
path = get_indexed_path(self.top_node, node)
return f"field_combo.{path}.next"
assert field.implements_storage
path = get_indexed_path(self.top_node, field)
return f"field_combo.{path}.{name}"
def get_counter_incr_identifier(self, field: 'FieldNode') -> str:
def get_counter_incr_strobe(self, field: 'FieldNode') -> str:
"""
Return the Veriog string that represents the field's inferred incr/decr strobe signal.
prop_ref will be either an incr or decr property reference, and it is already known that
the incr/decr properties are not explicitly set by the user and are therefore inferred.
Return the Verilog string that represents the field's incr strobe signal.
"""
# TODO: Implement this
raise NotImplementedError
prop_value = field.get_property('incr')
if prop_value:
return self.exp.dereferencer.get_value(prop_value)
def get_counter_decr_identifier(self, field: 'FieldNode') -> str:
# unset by the user, points to the implied input signal
return self.exp.hwif.get_implied_prop_input_identifier(field, "incr")
def get_counter_incrvalue(self, field: 'FieldNode') -> str:
"""
Return the Veriog string that represents the field's inferred incr/decr strobe signal.
prop_ref will be either an incr or decr property reference, and it is already known that
the incr/decr properties are not explicitly set by the user and are therefore inferred.
Return the string that represents the field's increment value
"""
# TODO: Implement this
raise NotImplementedError
incrvalue = field.get_property('incrvalue')
if incrvalue is not None:
return self.exp.dereferencer.get_value(incrvalue)
if field.get_property('incrwidth'):
return self.exp.hwif.get_implied_prop_input_identifier(field, "incrvalue")
return "1'b1"
def get_counter_incrsaturate_value(self, field: 'FieldNode') -> str:
prop_value = field.get_property('incrsaturate')
if prop_value is True:
return self.exp.dereferencer.get_value(2**field.width - 1)
return self.exp.dereferencer.get_value(prop_value)
def counter_incrsaturates(self, field: 'FieldNode') -> bool:
"""
Returns True if the counter saturates
"""
return field.get_property('incrsaturate') is not False
def get_counter_incrthreshold_value(self, field: 'FieldNode') -> str:
prop_value = field.get_property('incrthreshold')
if isinstance(prop_value, bool):
# No explicit value set. use max
return self.exp.dereferencer.get_value(2**field.width - 1)
return self.exp.dereferencer.get_value(prop_value)
def get_counter_decr_strobe(self, field: 'FieldNode') -> str:
"""
Return the Verilog string that represents the field's incr strobe signal.
"""
prop_value = field.get_property('decr')
if prop_value:
return self.exp.dereferencer.get_value(prop_value)
# unset by the user, points to the implied input signal
return self.exp.hwif.get_implied_prop_input_identifier(field, "decr")
def get_counter_decrvalue(self, field: 'FieldNode') -> str:
"""
Return the string that represents the field's decrement value
"""
decrvalue = field.get_property('decrvalue')
if decrvalue is not None:
return self.exp.dereferencer.get_value(decrvalue)
if field.get_property('decrwidth'):
return self.exp.hwif.get_implied_prop_input_identifier(field, "decrvalue")
return "1'b1"
def get_counter_decrsaturate_value(self, field: 'FieldNode') -> str:
prop_value = field.get_property('decrsaturate')
if prop_value is True:
return "'d0"
return self.exp.dereferencer.get_value(prop_value)
def counter_decrsaturates(self, field: 'FieldNode') -> bool:
"""
Returns True if the counter saturates
"""
return field.get_property('decrsaturate') is not False
def get_counter_decrthreshold_value(self, field: 'FieldNode') -> str:
prop_value = field.get_property('decrthreshold')
if isinstance(prop_value, bool):
# No explicit value set. use min
return "'d0"
return self.exp.dereferencer.get_value(prop_value)
def get_swacc_identifier(self, field: 'FieldNode') -> str:
"""

View File

@@ -39,8 +39,26 @@ class CombinationalStructGenerator(RDLStructGenerator):
self.add_member("load_next")
for signal in extra_combo_signals.values():
self.add_member(signal.name, signal.width)
if node.is_up_counter:
self.add_up_counter_members(node)
if node.is_down_counter:
self.add_down_counter_members(node)
self.pop_struct()
def add_up_counter_members(self, node: 'FieldNode') -> None:
self.add_member('incrthreshold')
if self.field_logic.counter_incrsaturates(node):
self.add_member('incrsaturate')
else:
self.add_member('overflow')
def add_down_counter_members(self, node: 'FieldNode') -> None:
self.add_member('decrthreshold')
if self.field_logic.counter_decrsaturates(node):
self.add_member('decrsaturate')
else:
self.add_member('underflow')
class FieldStorageStructGenerator(RDLStructGenerator):
@@ -92,10 +110,12 @@ class FieldLogicGenerator(RDLForLoopGenerator):
'node': node,
'reset': reset_value_str,
'field_path': get_indexed_path(self.exp.top_node, node),
'field_logic': self.field_logic,
'extra_combo_signals': extra_combo_signals,
'conditionals': conditionals,
'resetsignal': resetsignal,
'get_always_ff_event': get_always_ff_event,
'get_value': self.exp.dereferencer.get_value,
}
self.add_content(self.field_storage_template.render(context))
@@ -144,3 +164,16 @@ class FieldLogicGenerator(RDLForLoopGenerator):
self.add_content(
f"assign {output_identifier} = {value};"
)
if node.get_property('incrthreshold') is not False: # (explicitly not False. Not 0)
output_identifier = self.exp.hwif.get_implied_prop_output_identifier(node, "incrthreshold")
value = self.field_logic.get_field_combo_identifier(node, 'incrthreshold')
self.add_content(
f"assign {output_identifier} = {value};"
)
if node.get_property('decrthreshold') is not False: # (explicitly not False. Not 0)
output_identifier = self.exp.hwif.get_implied_prop_output_identifier(node, "decrthreshold")
value = self.field_logic.get_field_combo_identifier(node, 'decrthreshold')
self.add_content(
f"assign {output_identifier} = {value};"
)

View File

@@ -36,8 +36,8 @@ class HWSet(NextStateConditional):
next_val = "'1"
return [
f"field_combo.{field_path}.next = {next_val};",
f"field_combo.{field_path}.load_next = '1;",
f"next_c = {next_val};",
f"load_next_c = '1;",
]
@@ -71,6 +71,6 @@ class HWClear(NextStateConditional):
next_val = "'0"
return [
f"field_combo.{field_path}.next = {next_val};",
f"field_combo.{field_path}.load_next = '1;",
f"next_c = {next_val};",
f"load_next_c = '1;",
]

View File

@@ -39,8 +39,8 @@ class AlwaysWrite(NextStateConditional):
next_val = self.exp.hwif.get_input_identifier(field)
return [
f"field_combo.{field_path}.next = {next_val};",
f"field_combo.{field_path}.load_next = '1;",
f"next_c = {next_val};",
f"load_next_c = '1;",
]
class WEWrite(AlwaysWrite):

View File

@@ -24,8 +24,8 @@ class ClearOnRead(_OnRead):
def get_assignments(self, field: 'FieldNode') -> List[str]:
field_path = self.get_field_path(field)
return [
f"field_combo.{field_path}.next = '0;",
f"field_combo.{field_path}.load_next = '1;",
f"next_c = '0;",
f"load_next_c = '1;",
]
@@ -36,6 +36,6 @@ class SetOnRead(_OnRead):
def get_assignments(self, field: 'FieldNode') -> List[str]:
field_path = self.get_field_path(field)
return [
f"field_combo.{field_path}.next = '1;",
f"field_combo.{field_path}.load_next = '1;",
f"next_c = '1;",
f"load_next_c = '1;",
]

View File

@@ -40,8 +40,8 @@ class WriteOneSet(_OnWrite):
def get_assignments(self, field: 'FieldNode') -> List[str]:
field_path = self.get_field_path(field)
return [
f"field_combo.{field_path}.next = field_storage.{field_path} | {self._wr_data(field)};",
f"field_combo.{field_path}.load_next = '1;",
f"next_c = field_storage.{field_path} | {self._wr_data(field)};",
f"load_next_c = '1;",
]
class WriteOneClear(_OnWrite):
@@ -51,8 +51,8 @@ class WriteOneClear(_OnWrite):
def get_assignments(self, field: 'FieldNode') -> List[str]:
field_path = self.get_field_path(field)
return [
f"field_combo.{field_path}.next = field_storage.{field_path} & ~{self._wr_data(field)};",
f"field_combo.{field_path}.load_next = '1;",
f"next_c = field_storage.{field_path} & ~{self._wr_data(field)};",
f"load_next_c = '1;",
]
class WriteOneToggle(_OnWrite):
@@ -62,8 +62,8 @@ class WriteOneToggle(_OnWrite):
def get_assignments(self, field: 'FieldNode') -> List[str]:
field_path = self.get_field_path(field)
return [
f"field_combo.{field_path}.next = field_storage.{field_path} ^ {self._wr_data(field)};",
f"field_combo.{field_path}.load_next = '1;",
f"next_c = field_storage.{field_path} ^ {self._wr_data(field)};",
f"load_next_c = '1;",
]
class WriteZeroSet(_OnWrite):
@@ -73,8 +73,8 @@ class WriteZeroSet(_OnWrite):
def get_assignments(self, field: 'FieldNode') -> List[str]:
field_path = self.get_field_path(field)
return [
f"field_combo.{field_path}.next = field_storage.{field_path} | ~{self._wr_data(field)};",
f"field_combo.{field_path}.load_next = '1;",
f"next_c = field_storage.{field_path} | ~{self._wr_data(field)};",
f"load_next_c = '1;",
]
class WriteZeroClear(_OnWrite):
@@ -84,8 +84,8 @@ class WriteZeroClear(_OnWrite):
def get_assignments(self, field: 'FieldNode') -> List[str]:
field_path = self.get_field_path(field)
return [
f"field_combo.{field_path}.next = field_storage.{field_path} & {self._wr_data(field)};",
f"field_combo.{field_path}.load_next = '1;",
f"next_c = field_storage.{field_path} & {self._wr_data(field)};",
f"load_next_c = '1;",
]
class WriteZeroToggle(_OnWrite):
@@ -95,8 +95,8 @@ class WriteZeroToggle(_OnWrite):
def get_assignments(self, field: 'FieldNode') -> List[str]:
field_path = self.get_field_path(field)
return [
f"field_combo.{field_path}.next = field_storage.{field_path} ^ ~{self._wr_data(field)};",
f"field_combo.{field_path}.load_next = '1;",
f"next_c = field_storage.{field_path} ^ ~{self._wr_data(field)};",
f"load_next_c = '1;",
]
class WriteClear(_OnWrite):
@@ -106,8 +106,8 @@ class WriteClear(_OnWrite):
def get_assignments(self, field: 'FieldNode') -> List[str]:
field_path = self.get_field_path(field)
return [
f"field_combo.{field_path}.next = '0;",
f"field_combo.{field_path}.load_next = '1;",
f"next_c = '0;",
f"load_next_c = '1;",
]
class WriteSet(_OnWrite):
@@ -117,8 +117,8 @@ class WriteSet(_OnWrite):
def get_assignments(self, field: 'FieldNode') -> List[str]:
field_path = self.get_field_path(field)
return [
f"field_combo.{field_path}.next = '1;",
f"field_combo.{field_path}.load_next = '1;",
f"next_c = '1;",
f"load_next_c = '1;",
]
class Write(_OnWrite):
@@ -128,6 +128,6 @@ class Write(_OnWrite):
def get_assignments(self, field: 'FieldNode') -> List[str]:
field_path = self.get_field_path(field)
return [
f"field_combo.{field_path}.next = {self._wr_data(field)};",
f"field_combo.{field_path}.load_next = '1;",
f"next_c = {self._wr_data(field)};",
f"load_next_c = '1;",
]

View File

@@ -18,6 +18,6 @@ class Singlepulse(NextStateConditional):
def get_assignments(self, field: 'FieldNode') -> List[str]:
field_path = self.get_field_path(field)
return [
f"field_combo.{field_path}.next = '0;",
f"field_combo.{field_path}.load_next = '1;",
f"next_c = '0;",
f"load_next_c = '1;",
]

View File

@@ -0,0 +1,56 @@
{% macro up_counter(field) -%}
if({{field_logic.get_counter_incr_strobe(node)}}) begin // increment
{%- if field_logic.counter_incrsaturates(node) %}
if((({{node.width+1}})'(next_c) + {{field_logic.get_counter_incrvalue(node)}}) > {{field_logic.get_counter_incrsaturate_value(node)}}) begin // up-counter saturated
next_c = {{field_logic.get_counter_incrsaturate_value(node)}};
end else begin
next_c = next_c + {{field_logic.get_counter_incrvalue(node)}};
end
{%- else %}
field_combo.{{field_path}}.overflow = ((({{node.width+1}})'(next_c) + {{field_logic.get_counter_incrvalue(node)}}) > {{get_value(2**node.width - 1)}});
next_c = next_c + {{field_logic.get_counter_incrvalue(node)}};
{%- endif %}
load_next_c = '1;
{%- if not field_logic.counter_incrsaturates(node) %}
end else begin
field_combo.{{field_path}}.overflow = '0;
{%- endif %}
end
field_combo.{{field_path}}.incrthreshold = (field_storage.{{field_path}} >= {{field_logic.get_counter_incrthreshold_value(node)}});
{%- if field_logic.counter_incrsaturates(node) %}
field_combo.{{field_path}}.incrsaturate = (field_storage.{{field_path}} >= {{field_logic.get_counter_incrsaturate_value(node)}});
if(next_c > {{field_logic.get_counter_incrsaturate_value(node)}}) begin
next_c = {{field_logic.get_counter_incrsaturate_value(node)}};
load_next_c = '1;
end
{%- endif %}
{%- endmacro %}
{% macro down_counter(field) -%}
if({{field_logic.get_counter_decr_strobe(node)}}) begin // decrement
{%- if field_logic.counter_decrsaturates(node) %}
if(({{node.width+1}})'(next_c) < ({{field_logic.get_counter_decrvalue(node)}} + {{field_logic.get_counter_decrsaturate_value(node)}})) begin // down-counter saturated
next_c = {{field_logic.get_counter_decrsaturate_value(node)}};
end else begin
next_c = next_c - {{field_logic.get_counter_decrvalue(node)}};
end
{%- else %}
field_combo.{{field_path}}.underflow = (next_c < ({{field_logic.get_counter_decrvalue(node)}}));
next_c = next_c - {{field_logic.get_counter_decrvalue(node)}};
{%- endif %}
load_next_c = '1;
{%- if not field_logic.counter_decrsaturates(node) %}
end else begin
field_combo.{{field_path}}.underflow = '0;
{%- endif %}
end
field_combo.{{field_path}}.decrthreshold = (field_storage.{{field_path}} <= {{field_logic.get_counter_decrthreshold_value(node)}});
{%- if field_logic.counter_decrsaturates(node) %}
field_combo.{{field_path}}.decrsaturate = (field_storage.{{field_path}} <= {{field_logic.get_counter_decrsaturate_value(node)}});
if(next_c < {{field_logic.get_counter_decrsaturate_value(node)}}) begin
next_c = {{field_logic.get_counter_decrsaturate_value(node)}};
load_next_c = '1;
end
{%- endif %}
{%- endmacro %}

View File

@@ -1,7 +1,8 @@
{%- import 'field_logic/templates/counter_macros.sv' as counter_macros with context -%}
// Field: {{node.get_path()}}
always_comb begin
field_combo.{{field_path}}.next = field_storage.{{field_path}};
field_combo.{{field_path}}.load_next = '0;
automatic logic [{{node.width-1}}:0] next_c = field_storage.{{field_path}};
automatic logic load_next_c = '0;
{%- for signal in extra_combo_signals %}
field_combo.{{field_path}}.{{signal.name}} = {{signal.default_assignment}};
{%- endfor %}
@@ -12,6 +13,14 @@ always_comb begin
{%- endfor %}
end
{%- endfor %}
{%- if node.is_up_counter %}
{{counter_macros.up_counter(node)}}
{%- endif %}
{%- if node.is_down_counter %}
{{counter_macros.down_counter(node)}}
{%- endif %}
field_combo.{{field_path}}.next = next_c;
field_combo.{{field_path}}.load_next = load_next_c;
end
always_ff {{get_always_ff_event(resetsignal)}} begin
{% if resetsignal is not none -%}

View File

@@ -159,21 +159,23 @@ class Hwif:
contents.append(f"logic {prop_name};")
# Generate any implied counter inputs
if node.get_property('counter'):
if node.is_up_counter:
if not node.get_property('incr'):
# User did not provide their own incr component reference.
# Imply an input
contents.append("logic incr;")
if not node.get_property('decr'):
# User did not provide their own decr component reference.
# Imply an input
contents.append("logic decr;")
width = node.get_property('incrwidth')
if width:
# Implies a corresponding incrvalue input
contents.append(f"logic [{width-1}:0] incrvalue;")
if node.is_down_counter:
if not node.get_property('decr'):
# User did not provide their own decr component reference.
# Imply an input
contents.append("logic decr;")
width = node.get_property('decrwidth')
if width:
# Implies a corresponding decrvalue input
@@ -198,10 +200,15 @@ class Hwif:
contents.append(f"logic [{node.width-1}:0] value;")
# Generate output bit signals enabled via property
for prop_name in ["anded", "ored", "xored", "swmod", "swacc"]:
for prop_name in ["anded", "ored", "xored", "swmod", "swacc", "overflow", "underflow"]:
if node.get_property(prop_name):
contents.append(f"logic {prop_name};")
if node.get_property('incrthreshold') is not False: # (explicitly not False. Not 0)
contents.append("logic incrthreshold;")
if node.get_property('decrthreshold') is not False: # (explicitly not False. Not 0)
contents.append("logic decrthreshold;")
return contents
#---------------------------------------------------------------------------
@@ -252,7 +259,10 @@ class Hwif:
def get_implied_prop_input_identifier(self, field: FieldNode, prop: str) -> str:
assert prop in {'hwclr', 'hwset', 'swwe', 'swwel', 'we', 'wel'}
assert prop in {
'hwclr', 'hwset', 'swwe', 'swwel', 'we', 'wel',
'incr', 'decr', 'incrvalue', 'decrvalue'
}
path = get_indexed_path(self.top_node, field)
return "hwif_in." + path + "." + prop