Skip to content

Commit

Permalink
We had various deficiencies around sharing and overriding reset value…
Browse files Browse the repository at this point in the history
…s on

registers that share types. This provides the groundwork for better functioning
here and provides error messages when the user asks for something we don't support.
We also added new reset_0s and reset_1s functions to all register types so long
as they don't have enumerated types. This provides some additional easy knobs to
get alternate reset values for your register types.
  • Loading branch information
nathanaelhuffman committed Jan 24, 2025
1 parent 3067102 commit 65100a0
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 6 deletions.
26 changes: 26 additions & 0 deletions tools/site_cobble/rdl_pkg/listeners.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ class BaseListener(RDLListener):
def __init__(self):
self.prefix_stack = []
self.known_types = []
self.reset_byte_known_type = {}
self.cur_reg = None
self.registers = []

Expand Down Expand Up @@ -136,6 +137,31 @@ def exit_Reg(self, node):
# print(f"Exit reg: {node.inst_name}")
self.cur_reg.elaborate()
self.registers.append(self.cur_reg)
# If not a repeated type, put the reset value which we now know into a dictionary lookup
if not self.cur_reg.repeated_type:
self.reset_byte_known_type[self.cur_reg.type_name] = self.cur_reg.elaborated_reset, self.cur_reg.prefixed_name
else:
# If it was a repeated type, check that the reset value here matches the default reset value
base_reset, base_name = self.reset_byte_known_type.get(self.cur_reg.type_name)
reg_reset = self.cur_reg.elaborated_reset

# base reset is None, we'll allow 1s or 0s, nothing else. Anything else is an error
if base_reset is None and not (self.cur_reg.reset_is_all_1s or self.cur_reg.reset_is_all_0s):
raise ValueError(
f"Reset value '{reg_reset}'for register {self.cur_reg.prefixed_name} is not all 1s or all 0s, "
"and there's no default reset value for the type. The RDL subsystem doesn't support this pattern. "
"Please check that you can't use a default value for the type, or elide the special reset value"
" from the RDL and implement it in your own logic. You may also file an enhancement request to "
"figure out how to better support this pattern."
)
elif reg_reset != base_reset and base_reset is not None:
raise ValueError(
f"Reset value '{reg_reset}' for register {self.cur_reg.prefixed_name} does not match reset value '{base_reset}'"
f" already defined for that type: {base_name}.\n"
"This is likely due to a reset value override on an instance of the type that is conflicting "
f"with a default value on the shared type '{self.cur_reg.type_name}'"

)
self.cur_reg = None

def enter_Field(self, node) -> None:
Expand Down
67 changes: 61 additions & 6 deletions tools/site_cobble/rdl_pkg/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ class DuplicateEnumNameError(Exception):

class BaseModel:
@classmethod
def from_node(cls, node: RegNode, prefix_stack, repeated_type=False):
return cls(node=node, prefix_stack=prefix_stack, repeated_type=repeated_type)
def from_node(cls, node: RegNode, prefix_stack, repeated_type=False, base_reset=None):
return cls(node=node, prefix_stack=prefix_stack, repeated_type=repeated_type, base_reset=base_reset)

def __init__(self, **kwargs):
self.prefix = copy.deepcopy(kwargs.pop("prefix_stack"))
Expand Down Expand Up @@ -127,11 +127,42 @@ def encoded_fields(self):
Returns any register fields that have encodings
"""
return [x for x in self.packed_fields if x.has_encode()]

@property
def has_encoded_fields(self):
return len(self.encoded_fields) > 0

@property
def reset_is_all_1s(self):
for field in self.fields:
if (field.get_property("reset") is not None) and (not isinstance(field, ReservedField)):
expected_if_all_1s = 1 << (field.high - field.low + 1)
print(field.get_property("reset"), field.reset_1s)
if field.get_property("reset") != field.reset_1s:
return False
return True

@property
def reset_is_all_0s(self):
return self.elaborated_reset == 0

@property
def elaborated_reset(self):
"""
Returns the combined reset value of the register post elaboration, if it has one.
"""
if not self.has_reset_definition:
return None
reset_val = 0
for field in self.fields:
if (field.get_property("reset") is not None) and (not isinstance(field, ReservedField)):
reset_val |= field.get_property("reset") << field.low
return reset_val

@property
def has_reset_definition(self):
"""
RDS doesn't force a reset definition on registers but we may want to conditionally generate
RDL doesn't force a reset definition on registers but we may want to conditionally generate
reset logic if a reset value was specified.
"""
# Get the reset value for all the fields. If we don't see None in any of them we have defined reset behavior
Expand Down Expand Up @@ -225,20 +256,44 @@ def get_property(self, *args, **kwargs):
return prop

@property
def reset_str(self):
def reset_str(self) -> str:
my_rst = self.node.get_property("reset")
return "{:#0x}".format(my_rst) if my_rst is not None else "None"

@property
def vhdl_reset_or_default(self):
def vhdl_reset_or_default(self) -> str:
my_rst = self.node.get_property("reset")
reset_val = 0 if my_rst is None else my_rst
if self.width > 1:
return f'{self.width}x"{reset_val:X}"'
else:
return f"'{reset_val:1X}'"

@property
def reset_1s(self) -> int:
if self.width > 1:
return (1 << self.width) - 1
else:
return 1

@property
def vhdl_reset_1s(self) -> str:
if self.width > 1:
reset_val = (1 << self.width) - 1
return f'{self.width}x"{reset_val:X}"'
else:
reset_val = 1
return f"'{reset_val:1X}'"

@property
def vhdl_reset_0s(self) -> str:
reset_val = 0
if self.width > 1:
return f'{self.width}x"{reset_val:X}"'
else:
return f"'{reset_val:1X}'"

def has_encode(self):
def has_encode(self) -> bool:
return False

def __str__(self):
Expand Down
34 changes: 34 additions & 0 deletions tools/site_cobble/rdl_pkg/templates/regpkg_vhdl.jinja2
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@
-- sizeof() returns an integer of the number of used bits in the register
-- rec_reset abusing overload signatures to return the reset value for the
-- register type as defined in the RDL
-- reset_1s abusing overload signatures to return the reset value for the
-- register type with all defined bits set to 1
-- reset_0s abusing overload signatures to return the reset value for the
-- register type as defined bits set to 0
-- "or","and", "xor" we provide overloads for logical bitwise functions for
-- the record type with itself on both sides, it on the left side with an
-- slv on the right, and it on the left side with an unsigned on the right.
Expand Down Expand Up @@ -102,7 +106,15 @@ package {{module_name}} is
function compress (rec : {{reg_type_name}}) return std_logic_vector;
function uncompress (vec : std_logic_vector) return {{reg_type_name}};
function sizeof (rec : {{reg_type_name}}) return integer;
{# Only generate rec_reset if resets were specified #}
{% if register.has_reset_definition %}
function rec_reset return {{reg_type_name}};
{% endif %}
{# We can only generate reset1's and reset0's for non-enumerated types #}
{% if not register.has_encoded_fields %}
function reset_1s return {{reg_type_name}};
function reset_0s return {{reg_type_name}};
{% endif %}
function "or" (left, right : {{reg_type_name}}) return {{reg_type_name}};
function "or" (left : {{reg_type_name}}; right : std_logic_vector) return {{reg_type_name}};
function "or" (left : {{reg_type_name}}; right : unsigned) return {{reg_type_name}};
Expand Down Expand Up @@ -227,6 +239,8 @@ package body {{module_name}} is
begin
return {{register.used_bits}};
end sizeof;
{# Only generate rec_reset if resets were specified #}
{% if register.has_reset_definition %}
function rec_reset return {{reg_type_name}} is
variable ret_rec : {{reg_type_name}};
begin
Expand All @@ -239,6 +253,26 @@ package body {{module_name}} is
{% endfor %}
return ret_rec;
end rec_reset;
{% endif %}
{# We can only generate reset1's and reset0's for non-enumerated types #}
{% if not register.has_encoded_fields %}
function reset_1s return {{reg_type_name}} is
variable ret_rec : {{reg_type_name}};
begin
{% for field in register.packed_fields %}
ret_rec.{{field.name}} := {{field.vhdl_reset_1s}};
{% endfor %}
return ret_rec;
end reset_1s;
function reset_0s return {{reg_type_name}} is
variable ret_rec : {{reg_type_name}};
begin
{% for field in register.packed_fields %}
ret_rec.{{field.name}} := {{field.vhdl_reset_0s}};
{% endfor %}
return ret_rec;
end reset_0s;
{% endif %}
function "or" (left, right : {{reg_type_name}}) return {{reg_type_name}} is
variable ret_rec : {{reg_type_name}};
begin
Expand Down

0 comments on commit 65100a0

Please sign in to comment.