Source code for ifgen.svd.model.field
"""
A module implementing a data model for ARM CMSIS-SVD 'field' data.
"""
# built-in
from dataclasses import dataclass
from functools import cached_property
from typing import Any, Iterable, Optional
from xml.etree import ElementTree
# internal
from ifgen.svd.model.derived import DerivedMixin, derived_from_stack
from ifgen.svd.model.device import ARRAY_PROPERTIES
from ifgen.svd.model.enum import EnumeratedValues, get_enum
from ifgen.svd.string import StringKeyVal
[docs]
@dataclass
class Field(DerivedMixin):
"""A container for field information."""
derived_from: Optional["Field"]
enum: Optional[EnumeratedValues]
def __eq__(self, other) -> bool:
"""Determine if two fields are equivalent."""
result = False
if isinstance(other, Field):
our_data = self.ifgen_data
their_data = other.ifgen_data
result = (
our_data["index"] == their_data["index"]
and our_data["width"] == their_data["width"]
and our_data["read"] == their_data["read"]
and our_data["write"] == their_data["write"]
)
return result
@property
def access(self) -> str:
"""Get this instance's access property."""
return self.raw_data["access"]
[docs]
@classmethod
def string_keys(cls) -> Iterable[StringKeyVal]:
"""Get string keys for this instance type."""
# Not currently handling nested registers or clusters.
return ARRAY_PROPERTIES + [
StringKeyVal("name", True),
StringKeyVal("description", False),
# Mutually exclusive options:
# 1 (bitRangeLsbMsbStyle)
StringKeyVal("bitOffset", False),
StringKeyVal("bitWidth", False),
# 2 (bitRangeOffsetWidthStyle)
StringKeyVal("lsb", False),
StringKeyVal("msb", False),
# 3 (bitRangePattern)
StringKeyVal("bitRange", False),
# (end mutually exclusive options)
# is enum
StringKeyVal("access", False),
StringKeyVal("modifiedWriteValues", False),
# is enum
StringKeyVal("readAction", False),
]
@cached_property
def ifgen_data(self) -> dict[str, Any]:
"""Populate bit-field data."""
output: dict[str, Any] = {}
self.handle_description(output)
# We don't currently handle arrays of bit-fields.
assert "dim" not in self.raw_data
lsb = -1
msb = -1
if "bitRange" in self.raw_data:
msb_str, lsb_str = self.raw_data["bitRange"].split(":")
lsb = int(lsb_str.replace("]", ""))
msb = int(msb_str.replace("[", ""))
elif "lsb" in self.raw_data:
lsb = int(self.raw_data["lsb"])
msb = int(self.raw_data["msb"])
elif "bitOffset" in self.raw_data:
lsb = int(self.raw_data["bitOffset"])
msb = lsb + (int(self.raw_data["bitWidth"]) - 1)
assert lsb != -1 and msb != -1, self.raw_data
output["index"] = lsb
width = (msb - lsb) + 1
assert width >= 1, (msb, lsb, self.name)
output["width"] = width
output["read"] = "read" in self.access
output["write"] = "write" in self.access
return output
FieldMap = dict[str, Field]
[docs]
def get_fields(registers: ElementTree.Element) -> FieldMap:
"""Get register fields."""
result: FieldMap = {}
for field in derived_from_stack(registers.iterfind("field")):
derived_field = None
derived = field.attrib.get("derivedFrom")
if derived is not None:
derived_field = result[derived] # pragma: nocover
# Handle writeConstraint at some point?
# Handle enumerated values.
enum = None
enum_elem = field.find("enumeratedValues")
if enum_elem is not None:
enum = get_enum(enum_elem)
inst = Field.create(field, derived_field, enum)
result[inst.name] = inst
return result