Source code for ifgen.svd.model.peripheral

"""
A module implementing a data model for ARM CMSIS-SVD 'peripheral' data.
"""

# built-in
from dataclasses import dataclass
from typing import Iterable, Optional, Union
from xml.etree import ElementTree

# internal
from ifgen.svd.model.address_block import AddressBlock
from ifgen.svd.model.derived import DerivedMixin
from ifgen.svd.model.device import ARRAY_PROPERTIES, REGISTER_PROPERTIES
from ifgen.svd.model.field import FieldMap
from ifgen.svd.model.interrupt import Interrupt
from ifgen.svd.string import StringKeyVal

RegisterData = list[Union["Register", "Cluster"]]


[docs] @dataclass class Cluster(DerivedMixin): """A container for cluster information.""" derived_from: Optional["Cluster"] children: RegisterData peripheral: "Peripheral" def __eq__(self, other) -> bool: """Determine if two clusers are equivalent.""" return isinstance(other, Cluster) and all( x == y for x, y in zip(self.children, other.children) )
[docs] @classmethod def string_keys(cls) -> Iterable[StringKeyVal]: """Get string keys for this instance type.""" return ( ARRAY_PROPERTIES + [ StringKeyVal("name", True), StringKeyVal("description", False), StringKeyVal("alternateCluster", False), StringKeyVal("headerStructName", False), StringKeyVal("addressOffset", True), ] + REGISTER_PROPERTIES )
[docs] def fields_equal(left: Optional[FieldMap], right: Optional[FieldMap]) -> bool: """Determine if two field maps are equivalent.""" result = left is None and right is None if left is not None and right is not None and len(left) == len(right): for lkey, lvalue in left.items(): result = lkey in right and lvalue == right[lkey] if not result: break return result
[docs] @dataclass class Register(DerivedMixin): """A container for register information.""" derived_from: Optional["Register"] fields: Optional[FieldMap] peripheral: "Peripheral" def __eq__(self, other) -> bool: """Determine if two registers are equivalent.""" return isinstance(other, Register) and fields_equal( self.fields, other.fields ) @property def bits(self) -> int: """Get the size of this register in bits.""" result = self.raw_data.get("size", self.peripheral.bits) assert result is not None, (self.name, self.peripheral.name) return int(result) @property def size(self) -> int: """Get the size of this register in bytes.""" return self.bits // 8 @property def alternate_group(self) -> Optional[str]: """Get this register's possible alternate group.""" return self.raw_data.get("alternateGroup") @property def access(self) -> str: """Get the access setting for this register.""" access = self.raw_data.get("access", self.peripheral.access) read = False write = False if access is None and self.fields is not None: for field in self.fields.values(): if "access" in field.raw_data: read |= "read" in field.raw_data["access"] write |= "write" in field.raw_data["access"] if read and not write: return "read-only" if write and not read: return "write-only" return "read-write" @property def c_type(self) -> str: """Get the C type for this register.""" return f"uint{self.bits}_t"
[docs] @classmethod def string_keys(cls) -> Iterable[StringKeyVal]: """Get string keys for this instance type.""" return ( ARRAY_PROPERTIES + [ StringKeyVal("name", True), StringKeyVal("displayName", False), StringKeyVal("description", False), StringKeyVal("alternateGroup", False), StringKeyVal("alternateRegister", False), StringKeyVal("addressOffset", True), ] + REGISTER_PROPERTIES + [ # is enum StringKeyVal("dataType", False), # is enum StringKeyVal("modifiedWriteValues", False), # is enum StringKeyVal("readAction", False), ] )
[docs] def register_groups(registers: RegisterData) -> dict[str, list[Register]]: """Get groups of registers.""" result: dict[str, list[Register]] = {} for item in registers: if isinstance(item, Register): alt = item.alternate_group if alt: if alt not in result: result[alt] = [] result[alt].append(item) return result
[docs] @dataclass class Peripheral(DerivedMixin): """A container for peripheral information.""" derived_from: Optional["Peripheral"] # Currently treated as metadata. interrupts: list[Interrupt] address_blocks: list[AddressBlock] registers: RegisterData system_width_bits: Optional[int]
[docs] def register_groups(self) -> dict[str, list[Register]]: """Get register groups.""" return register_groups(self.registers)
def __eq__(self, other) -> bool: """Determine if two peripherals are equivalent.""" return isinstance(other, Peripheral) and all( x == y for x, y in zip(self.registers, other.registers) ) @property def bits(self) -> Optional[int]: """Get size for this peripheral in bits.""" result = self.derived_elem.raw_data.get("size", self.system_width_bits) return int(result) if result is not None else None @property def access(self) -> Optional[str]: """Get the possible 'access' field default.""" return self.derived_elem.raw_data.get("access")
[docs] def base_name(self, lower: bool = True) -> str: """Get the base peripheral name.""" result = self.name result = result.lower() if lower else result return result
[docs] def handle_address_block(self, address_block: ElementTree.Element) -> None: """Handle an 'address_block' element.""" self.address_blocks.append(AddressBlock.create(address_block))
[docs] def handle_interrupt(self, interrupt: ElementTree.Element) -> None: """Handle an 'interrupt' element.""" self.interrupts.append(Interrupt.create(interrupt))
[docs] @classmethod def string_keys(cls) -> Iterable[StringKeyVal]: """Get string keys for this instance type.""" return ( ARRAY_PROPERTIES + [ StringKeyVal("name", True), StringKeyVal("version", False), StringKeyVal("description", False), StringKeyVal("alternatePeripheral", False), StringKeyVal("groupName", False), StringKeyVal("prependToName", False), StringKeyVal("appendToName", False), StringKeyVal("headerStructName", False), StringKeyVal("disableCondition", False), StringKeyVal("baseAddress", True), ] + REGISTER_PROPERTIES )