Source code for ifgen.svd.group.fields

"""
A module for generating configuration data for struct fields.
"""

# built-in
from typing import Any, Iterable, Optional

# internal
from ifgen.svd.group.enums import ENUM_DEFAULTS, get_enum_name, translate_enums
from ifgen.svd.model.peripheral import (
    Cluster,
    Register,
    RegisterData,
    register_groups,
)

StructMap = dict[str, Any]
StructField = dict[str, Any]


[docs] def parse_offset(data: dict[str, str]) -> int: """Parse a hex string to get decimal address offset.""" return int(data["addressOffset"], 16)
[docs] def check_not_handled_fields( data: dict[str, Any], fields: Iterable[str] ) -> None: """Ensure that some keys aren't present in data.""" for field in fields: assert ( field not in data ), f"Field '{field}' isn't currently handled: {data}."
EnumMap = dict[str, Any]
[docs] def handle_cluster( cluster: Cluster, structs: StructMap, enums: EnumMap, peripheral: str, min_enum_members: int, ) -> tuple[int, StructField]: """Handle a cluster element.""" # Ensure that a correct result will be produced. check_not_handled_fields(cluster.raw_data, ["alernateCluster"]) # Register a struct for this cluster. Should we use a namespace for this? cluster_struct: dict[str, Any] = cluster.handle_description() size, cluster_struct["fields"] = struct_fields( cluster.children, structs, enums, peripheral, min_enum_members ) # Too difficult due to padding (may need to comment out). Padding within # a cluster isn't currently handled. # cluster_struct["expected_size"] = size raw_name = sanitize_name(cluster.name) cluster_name = cluster.raw_data.get( "headerStructName", f"{raw_name}_instance" ) structs[cluster_name] = cluster_struct # This needs to be an array element somehow. Use a namespace? # dimIncrement not handled. array_dim = int(cluster.raw_data.get("dim", 1)) size *= array_dim result: StructField = { "name": raw_name, "type": cluster_name, # Too difficult due to padding (may need to comment out). # "expected_size": size, # "expected_offset": parse_offset(cluster.raw_data), } if array_dim > 1: result["array_length"] = array_dim cluster.handle_description(result) return size, result
RegisterMap = dict[str, Register]
[docs] def process_bit_fields( register: Register, output: dict[str, Any], enums: EnumMap, peripheral: str, min_enum_width: int, ) -> None: """Get bit-field declarations for a given register.""" if register.fields is None: return result: list[dict[str, Any]] = [] # Process fields. for name, field in register.fields.items(): field_data: dict[str, Any] = {"name": name} field_data.update(field.ifgen_data) result.append(field_data) # Handle creating an enumeration. if field.enum is not None: # Register enumeration. raw = translate_enums(field.enum) if field_data["width"] >= min_enum_width: new_enum: dict[str, Any] = {"enum": raw} new_enum.update(ENUM_DEFAULTS) # Increase size of underlying if necessary. if field_data["width"] > 8: new_enum["underlying"] = "uint16_t" # Check if enum is unique. enum_name = get_enum_name( sanitize_name(f"{peripheral}_{register.name}_{name}"), peripheral, raw, ) field_data["type"] = enum_name if enum_name not in enums: enums[enum_name] = new_enum if result: output["fields"] = result
[docs] def sanitize_name(name: str) -> str: """Remove special characters from a name.""" return name.replace("[%s]", "").replace("%s", "")
[docs] def handle_register( register: Register, register_map: RegisterMap, enums: EnumMap, peripheral: str, min_enum_width: int, ) -> tuple[int, StructField]: """Handle a register entry.""" # Handle adding a union entry to the main field and handle this register's # bit fields. if "alternateRegister" in register.raw_data: return 0, {} # Currently provided in metadata output. # check_not_handled_fields(register.raw_data, ["alternateGroup"]) # dimIncrement not handled. array_dim = int(register.raw_data.get("dim", 1)) size = register.size * array_dim data = { "name": sanitize_name(register.name), "type": register.c_type, "expected_size": size, "expected_offset": parse_offset(register.raw_data), } if array_dim > 1: data["array_length"] = array_dim access = register.access if access == "read-only": data["const"] = True notes = [access] register.handle_description(data, prefix=f"({', '.join(notes)}) ") # Handle bit fields. process_bit_fields(register, data, enums, peripheral, min_enum_width) # Handle alternates. alts = register.alternates if alts: alts_data: list[dict[str, Any]] = [] for item in alts: alt_data: dict[str, Any] = {"name": item.name} if item.access == "read-only": alt_data["const"] = True item.handle_description(alt_data, prefix=f"({item.access}) ") process_bit_fields( register_map[item.name], alt_data, enums, peripheral, min_enum_width, ) # Forward array information (might be necessary at some point). # if "array_length" in data: # alt_data["array_length"] = data["array_length"] alts_data.append(alt_data) data["alternates"] = alts_data return size, data
[docs] def handle_register_group( name: str, registers: list[Register], register_map: RegisterMap, enums: EnumMap, peripheral: str, min_enum_width: int, ) -> tuple[int, StructField]: """Handle creating a struct field for a group of registers.""" # Handle melding register data at some point (aggregate more bit fields). del name return handle_register( registers[0], register_map, enums, peripheral, min_enum_width )
[docs] def handle_item( item: Register | Cluster, structs: StructMap, enums: EnumMap, peripheral: str, min_enum_width: int, groups_handled: set[str], register_map: RegisterMap, groups: dict[str, list[Register]], by_name: dict[str, str], ) -> tuple[int, Optional[StructField]]: """Handle creating a struct field from a register or cluster.""" inst_size = 0 field = None if isinstance(item, Cluster): inst_size, field = handle_cluster( item, structs, enums, peripheral, min_enum_width ) else: # Handle register groups. if item.name in by_name: group = by_name[item.name] if group not in groups_handled: inst_size, field = handle_register_group( group, groups[group], register_map, enums, peripheral, min_enum_width, ) groups_handled.add(group) # Handle normal registers. else: inst_size, field = handle_register( item, register_map, enums, peripheral, min_enum_width ) return inst_size, field
[docs] def struct_fields( registers: RegisterData, structs: StructMap, enums: EnumMap, peripheral: str, min_enum_width: int, size: int = None, ) -> tuple[int, list[StructField]]: """Generate data for struct fields.""" fields = [] if size is None: size = 0 # Create a string mapping of registers. register_map: RegisterMap = {} for item in registers: if isinstance(item, Register): register_map[item.name] = item groups = register_groups(registers) by_name = {} for group, regs in groups.items(): for reg in regs: by_name[reg.name] = group groups_handled: set[str] = set() for item in registers: inst_size, field = handle_item( item, structs, enums, peripheral, min_enum_width, groups_handled, register_map, groups, by_name, ) if inst_size > 0 and field is not None: fields.append(field) size += inst_size return size, fields