User-Defined Properties
The SystemRDL standard allows users to extend components with custom properties. These UDPs are declared within the user’s SystemRDL source code prior to use. Inevitably, these UDPs are processed by a downstream tool that has some well-defined semantics for these language extensions. This compiler provides an API that allows tool develoers to define additional validation for these properties.
Pre-registering a UDP
UDP semantics can be pre-loaded into the compiler namespace as a way to formalize extensions to SystemRDL that your tool supports.
Registration of a UDP is done as follows:
from systemrdl.udp import UDPDefinition
from systemrdl.components import Field, Signal
# 1. Describe your UDP
class MyUDPDefinition(UDPDefinition):
name = "my_udp"
valid_components = {Field, Signal}
valid_type = int
# Optional callback that validates values assigned to 'my_udp'
def validate(self, node, value):
if(value == 42):
self.msg.error(
"The value assigned to 'my_udp' cannot be 42! That number is reserved.",
self.get_src_ref(node)
)
# 2. Register it with your RDLCompiler instance
rdlc.register_udp(MyUDPDefinition)
The above definition is equivalent to the following SystemRDL:
property my_udp {
type = longint unsigned;
component = field | signal;
};
Soft UDPs
By default, register_udp()
registers
UDPs as “soft” definitions. Soft UDP definitions behave as follows:
The UDP is not available to be used until it is explicitly defined in the SystemRDL source. If a user attempts to use a soft UDP prior to it being declared, the compiler will flag an error.
Upon definition, the user’s declaration shall be equivalent to the pre-registered definition. If the user’s declaration does not match, the compiler will flag an error. This ensures that the user’s declaration matches the expectations of your tool.
If the user’s RDL source never defines the UDP, querying it via
node.get_property()
will gracefully return its unassigned default (as defined byget_unassigned_default()
) instead of aLookupError
exception. This simplifies how tool developers interact with users’ RDL code.Once defined by the user in RDL source, the UDP is no longer considered ‘soft’, and can be assigned normally.
Important
Although it may initially seem like more steps for the end-user, having them declare the UDP in the RDL source is preferred over pre-declaring “hard” UDPs within your tool.
Silently declaring hard UDPs not recommended since it encourages users to write SystemRDL that uses UDP extensions that are not formally declared in the RDL source. This bends the rules of the SystemRDL specification and hurts the cross-vendor compatibility of your users’ SystemRDL source code.
Using soft UDPs has the benefit of enforcing that the user defines and uses UDPs correctly whilst not violating the official SystemRDL language specification.
The UDPDefinition descriptor
The full details of the UDPDefinition
class is as follows:
Class variables and methods that you can define
- class systemrdl.udp.UDPDefinition(env: RDLEnvironment)
UDP definition descriptor class.
- name = ''
User-defined property name
- valid_components = {<class 'systemrdl.component.Field'>, <class 'systemrdl.component.Reg'>, <class 'systemrdl.component.Signal'>, <class 'systemrdl.component.Regfile'>, <class 'systemrdl.component.Addrmap'>, <class 'systemrdl.component.Mem'>}
Set of
Component
types the UDP can be bound to. By default, the UDP can be bound to all components.
- valid_type = None
Data type of the assignment value that this UDP will enforce. If this is a reference, either specify the specific component type class (eg.
Field
), or the generic representation of all references:RefType
- default_assignment = None
Specifies the value assigned if a value is not specified when the UDP is bound to a component. Value must be compatible with
valid_type
- constr_componentwidth = False
If set to True, enables a validation check that enforces that the assigned value of the property shall not have a value of 1 for any bit beyond the width of the field. This can only be used if
valid_type
isint
- validate(node: Node, value: Any) None
Optional user-defined validation function.
This function is called after design elaboration on every assignment of the user defined property. This provides a mechanism to further validate the value assigend to your user-defined property.
If the user-assigned value fails your validation test, be sure to flag it as an error as follows:
self.msg.error("My error message", self.get_src_ref(node))
- get_unassigned_default(node: Node) Any
According to the SystemRDL spec, if a user-defined property is not explicitly assigned, then it does not get bound with any implied default value.
For convenience to developers, this callback allows you to specify an implied default value if the UDP was never explicitly assigned. This only affects the behavior of
Node.get_property()
and does not change the semantics of how SystemRDL is interpreted during compilaton.
Utilities
- class systemrdl.udp.UDPDefinition(env: RDLEnvironment)
UDP definition descriptor class.
- get_src_ref(node: Node) SourceRefBase
Get the src_ref object for this property assignment.
This function is useful when emitting error messages from within
validate()
.
- property msg: MessageHandler
Reference to the compiler’s message handler. Use this to emit error messages from within
validate()
.