From 8d4072371923e6a139b4b19e4a79fe83ebc36a4b Mon Sep 17 00:00:00 2001 From: Vaughn Kottler Date: Thu, 19 Oct 2023 23:44:26 -0500 Subject: [PATCH 1/2] 3.2.0 - Some config and other improvements --- .github/workflows/python-package.yml | 2 +- README.md | 4 +- config | 2 +- ifgen/__init__.py | 4 +- ifgen/commands/svd.py | 18 +++++++- ifgen/config/__init__.py | 31 +++++++++++++ ifgen/data/default.yaml | 4 ++ ifgen/data/schemas/Config.yaml | 63 +++++++++++++++++++++------ ifgen/data/schemas/Enum.yaml | 7 ++- ifgen/data/schemas/Struct.yaml | 11 ++--- ifgen/data/schemas/has_unit_test.yaml | 5 --- ifgen/enum/header.py | 2 +- ifgen/struct/__init__.py | 5 ++- ifgen/svd/group/__init__.py | 16 +++++-- ifgen/svd/group/fields.py | 55 ++++++++++++++--------- ifgen/svd/task.py | 15 +++++-- local/variables/package.yaml | 4 +- pyproject.toml | 2 +- 18 files changed, 182 insertions(+), 68 deletions(-) delete mode 100644 ifgen/data/schemas/has_unit_test.yaml diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 7b67fe8..73f2c2d 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -77,7 +77,7 @@ jobs: - run: | mk python-release owner=vkottler \ - repo=ifgen version=3.1.7 + repo=ifgen version=3.2.0 if: | matrix.python-version == '3.11' && matrix.system == 'ubuntu-latest' diff --git a/README.md b/README.md index ac684b0..43ecbb0 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,11 @@ ===================================== generator=datazen version=3.1.4 - hash=d3a7ef970e2a79e369d20b7074001b70 + hash=d2c1e9d24480cbe7d72337b49a9dc366 ===================================== --> -# ifgen ([3.1.7](https://pypi.org/project/ifgen/)) +# ifgen ([3.2.0](https://pypi.org/project/ifgen/)) [![python](https://img.shields.io/pypi/pyversions/ifgen.svg)](https://pypi.org/project/ifgen/) ![Build Status](https://github.com/vkottler/ifgen/workflows/Python%20Package/badge.svg) diff --git a/config b/config index b9d7325..0c555bf 160000 --- a/config +++ b/config @@ -1 +1 @@ -Subproject commit b9d73256fb9357f8f6152b68df50c9748cbf4a7f +Subproject commit 0c555bf6565cc5d90408adbad3c162edca43a7e8 diff --git a/ifgen/__init__.py b/ifgen/__init__.py index c5d6a40..be48e02 100644 --- a/ifgen/__init__.py +++ b/ifgen/__init__.py @@ -1,7 +1,7 @@ # ===================================== # generator=datazen # version=3.1.4 -# hash=8dba39fd4ab323a71ee884c236534c9f +# hash=7e93d7f1d0e747a4ad065db36864eb77 # ===================================== """ @@ -10,4 +10,4 @@ DESCRIPTION = "An interface generator for distributed computing." PKG_NAME = "ifgen" -VERSION = "3.1.7" +VERSION = "3.2.0" diff --git a/ifgen/commands/svd.py b/ifgen/commands/svd.py index 93344dd..ab6e081 100644 --- a/ifgen/commands/svd.py +++ b/ifgen/commands/svd.py @@ -38,11 +38,16 @@ def svd_cmd(args: _Namespace) -> int: enums.PRUNE_ENUMS = enable_pruning base.PRUNE_STRUCTS = enable_pruning - SvdProcessingTask.svd(path).generate_configs(args.output) + SvdProcessingTask.svd(path, args.min_enum_members).generate_configs( + args.output + ) return 0 +DEFAULT_MIN_ENUMS = 3 + + def add_svd_cmd(parser: _ArgumentParser) -> _CommandFunction: """Add svd-command arguments to its parser.""" @@ -54,6 +59,17 @@ def add_svd_cmd(parser: _ArgumentParser) -> _CommandFunction: help="output directory for configuration files", ) + parser.add_argument( + "-m", + "--min-enum_members", + type=int, + default=DEFAULT_MIN_ENUMS, + help=( + "minimum number of enumeration elements to warrant " + "generating an enumeration definition (default: %(default)s)" + ), + ) + parser.add_argument( "svd_file", type=str, help="path/uri to a CMSIS-SVD file" ) diff --git a/ifgen/config/__init__.py b/ifgen/config/__init__.py index e6606bd..ee1656b 100644 --- a/ifgen/config/__init__.py +++ b/ifgen/config/__init__.py @@ -2,11 +2,15 @@ A module implementing a configuration interface for the package. """ +# built-in +from typing import Any + # third-party from vcorelib.dict import merge from vcorelib.dict.codec import BasicDictCodec as _BasicDictCodec from vcorelib.io import ARBITER as _ARBITER from vcorelib.io import DEFAULT_INCLUDES_KEY +from vcorelib.io.types import JsonObject as _JsonObject from vcorelib.paths import Pathlike, find_file # internal @@ -17,6 +21,33 @@ class Config(IfgenDictCodec, _BasicDictCodec): """The top-level configuration object for the package.""" + def init(self, data: _JsonObject) -> None: + """Initialize this instance.""" + + super().init(data) + + common = ["identifier", "unit_test"] + + # Forward enum settings. + enum_forwards = common + ["use_map"] + enum: dict[str, Any] + for enum in data.get("enums", {}).values(): # type: ignore + for forward in enum_forwards: + enum.setdefault( + forward, + data["enum"][forward], # type: ignore + ) + + # Forward struct settings. + struct_forwards = common + ["codec", "stream", "methods"] + struct: dict[str, Any] + for struct in data.get("structs", {}).values(): # type: ignore + for forward in struct_forwards: + struct.setdefault( + forward, + data["struct"][forward], # type: ignore + ) + def load(path: Pathlike) -> Config: """Load a configuration object.""" diff --git a/ifgen/data/default.yaml b/ifgen/data/default.yaml index c5288b4..07ef63a 100644 --- a/ifgen/data/default.yaml +++ b/ifgen/data/default.yaml @@ -1,5 +1,9 @@ --- +struct: {} structs: {} +enum: {} +enums: {} + ifgen: common: {} diff --git a/ifgen/data/schemas/Config.yaml b/ifgen/data/schemas/Config.yaml index 340841e..1908350 100644 --- a/ifgen/data/schemas/Config.yaml +++ b/ifgen/data/schemas/Config.yaml @@ -36,21 +36,41 @@ properties: "^[a-zA-Z0-9-_.]+$": $ref: package://ifgen/schemas/Struct.yaml - struct_id_underlying: &id_underlying - type: string - default: uint16_t - enum: - - int8_t - - int16_t - - int32_t - - int64_t + # Defaults for struct generation. + struct: + type: object + additionalProperties: false + required: [] + properties: + codec: + type: boolean + default: true + stream: + type: boolean + default: true + methods: + type: boolean + default: true + identifier: + type: boolean + default: true + unit_test: + type: boolean + default: true - - uint8_t - - uint16_t - - uint32_t - - uint64_t + id_underlying: &id_underlying + type: string + default: uint16_t + enum: + - int8_t + - int16_t + - int32_t + - int64_t - enum_id_underlying: *id_underlying + - uint8_t + - uint16_t + - uint32_t + - uint64_t enums: type: object @@ -59,5 +79,22 @@ properties: "^[a-zA-Z0-9-_.]+$": $ref: package://ifgen/schemas/Enum.yaml + # Defaults for enum generation. + enum: + type: object + additionalProperties: false + required: [] + properties: + id_underlying: *id_underlying + use_map: + type: boolean + default: true + identifier: + type: boolean + default: true + unit_test: + type: boolean + default: true + ifgen: type: object diff --git a/ifgen/data/schemas/Enum.yaml b/ifgen/data/schemas/Enum.yaml index 40f2039..0293b34 100644 --- a/ifgen/data/schemas/Enum.yaml +++ b/ifgen/data/schemas/Enum.yaml @@ -3,7 +3,6 @@ includes: - has_description.yaml - has_namespace.yaml - has_json_indent.yaml - - has_unit_test.yaml required: [enum] @@ -22,13 +21,13 @@ properties: description: type: string + # Provide defaults from a higher-level configuration. use_map: type: boolean - default: true - identifier: type: boolean - default: true + unit_test: + type: boolean underlying: type: string diff --git a/ifgen/data/schemas/Struct.yaml b/ifgen/data/schemas/Struct.yaml index 27c9826..6c59a30 100644 --- a/ifgen/data/schemas/Struct.yaml +++ b/ifgen/data/schemas/Struct.yaml @@ -4,26 +4,21 @@ includes: - has_namespace.yaml - has_json_indent.yaml - has_expected_size.yaml - - has_unit_test.yaml required: [fields] properties: + # Provide defaults from a higher-level configuration. codec: type: boolean - default: true - stream: type: boolean - default: true - methods: type: boolean - default: true - identifier: type: boolean - default: true + unit_test: + type: boolean instances: type: array diff --git a/ifgen/data/schemas/has_unit_test.yaml b/ifgen/data/schemas/has_unit_test.yaml deleted file mode 100644 index 7862f3b..0000000 --- a/ifgen/data/schemas/has_unit_test.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -properties: - unit_test: - type: boolean - default: true diff --git a/ifgen/enum/header.py b/ifgen/enum/header.py index 5896536..3584a99 100644 --- a/ifgen/enum/header.py +++ b/ifgen/enum/header.py @@ -57,7 +57,7 @@ def enum_header(task: GenerateTask, writer: IndentedFileWriter) -> None: writer.write( ( "static constexpr " - f"{task.env.config.data['enum_id_underlying']} " + f"{task.env.config.data['enum']['id_underlying']} " f"{task.name}_id = {runtime.id};" ) ) diff --git a/ifgen/struct/__init__.py b/ifgen/struct/__init__.py index ed49453..710a308 100644 --- a/ifgen/struct/__init__.py +++ b/ifgen/struct/__init__.py @@ -190,10 +190,13 @@ def create_struct(task: GenerateTask) -> None: style=CommentStyle.C_DOXYGEN ) as lines: if task.instance["identifier"]: + underlying = task.env.config.data["struct"][ + "id_underlying" + ] lines.append( ( "static constexpr " - f"{task.env.config.data['struct_id_underlying']} " + f"{underlying} " f"id = {task.protocol().id};", f"{task.name}'s identifier.", ) diff --git a/ifgen/svd/group/__init__.py b/ifgen/svd/group/__init__.py index b8cff12..2fe9dd8 100644 --- a/ifgen/svd/group/__init__.py +++ b/ifgen/svd/group/__init__.py @@ -32,7 +32,10 @@ def struct_instance(peripheral: Peripheral) -> dict[str, Any]: def struct_data( - group: PeripheralGroup, structs: StructMap, enums: EnumMap + group: PeripheralGroup, + structs: StructMap, + enums: EnumMap, + min_enum_members: int, ) -> dict[str, Any]: """Get struct data for a peripheral group.""" @@ -42,7 +45,11 @@ def struct_data( data["instances"] = [struct_instance(x) for x in group.peripherals] size, data["fields"] = struct_fields( - peripheral.registers, structs, enums, peripheral.base_name(lower=False) + peripheral.registers, + structs, + enums, + peripheral.base_name(lower=False), + min_enum_members, ) # Too difficult due to padding. @@ -57,6 +64,7 @@ def handle_group( output_dir: Path, group: PeripheralGroup, includes: set[Path], + min_enum_members: int, ) -> None: """Handle a peripheral group.""" @@ -65,5 +73,7 @@ def handle_group( structs: StructMap = {} enums: EnumMap = {} - structs[group.root.base_name()] = struct_data(group, structs, enums) + structs[group.root.base_name()] = struct_data( + group, structs, enums, min_enum_members + ) ARBITER.encode(output, {"structs": structs, "enums": enums}) diff --git a/ifgen/svd/group/fields.py b/ifgen/svd/group/fields.py index e9d335a..8ba2727 100644 --- a/ifgen/svd/group/fields.py +++ b/ifgen/svd/group/fields.py @@ -41,7 +41,11 @@ def check_not_handled_fields( def handle_cluster( - cluster: Cluster, structs: StructMap, enums: EnumMap, peripheral: str + cluster: Cluster, + structs: StructMap, + enums: EnumMap, + peripheral: str, + min_enum_members: int, ) -> tuple[int, StructField]: """Handle a cluster element.""" @@ -51,7 +55,7 @@ def handle_cluster( # 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 + cluster.children, structs, enums, peripheral, min_enum_members ) # Too difficult due to padding (may need to comment out). @@ -91,6 +95,7 @@ def process_bit_fields( output: dict[str, Any], enums: EnumMap, peripheral: str, + min_enum_members: int, ) -> None: """Get bit-field declarations for a given register.""" @@ -109,22 +114,24 @@ def process_bit_fields( if field.enum is not None: # Register enumeration. raw = translate_enums(field.enum) - 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" + if len(raw) >= min_enum_members: + new_enum: dict[str, Any] = {"enum": raw} + new_enum.update(ENUM_DEFAULTS) - # Check if enum is unique. - enum_name = get_enum_name( - f"{peripheral}_{register.name}_{name}".replace("[%s]", ""), - peripheral, - raw, - ) - field_data["type"] = enum_name - if enum_name not in enums: - enums[enum_name] = new_enum + # 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( + f"{peripheral}_{register.name}_{name}".replace("[%s]", ""), + peripheral, + raw, + ) + field_data["type"] = enum_name + if enum_name not in enums: + enums[enum_name] = new_enum if result: output["fields"] = result @@ -135,6 +142,7 @@ def handle_register( register_map: RegisterMap, enums: EnumMap, peripheral: str, + min_enum_members: int, ) -> tuple[int, StructField]: """Handle a register entry.""" @@ -167,7 +175,7 @@ def handle_register( register.handle_description(data, prefix=f"({', '.join(notes)}) ") # Handle bit fields. - process_bit_fields(register, data, enums, peripheral) + process_bit_fields(register, data, enums, peripheral, min_enum_members) # Handle alternates. alts = register.alternates @@ -181,7 +189,11 @@ def handle_register( item.handle_description(alt_data, prefix=f"({item.access}) ") process_bit_fields( - register_map[item.name], alt_data, enums, peripheral + register_map[item.name], + alt_data, + enums, + peripheral, + min_enum_members, ) # Forward array information (might be necessary at some point). @@ -200,6 +212,7 @@ def struct_fields( structs: StructMap, enums: EnumMap, peripheral: str, + min_enum_members: int, size: int = None, ) -> tuple[int, list[StructField]]: """Generate data for struct fields.""" @@ -217,9 +230,11 @@ def struct_fields( for item in registers: inst_size, field = ( - handle_cluster(item, structs, enums, peripheral) + handle_cluster(item, structs, enums, peripheral, min_enum_members) if isinstance(item, Cluster) - else handle_register(item, register_map, enums, peripheral) + else handle_register( + item, register_map, enums, peripheral, min_enum_members + ) ) if inst_size > 0: fields.append(field) diff --git a/ifgen/svd/task.py b/ifgen/svd/task.py index eeee996..492afb1 100644 --- a/ifgen/svd/task.py +++ b/ifgen/svd/task.py @@ -30,16 +30,17 @@ class SvdProcessingTask: """A container for SVD-processing state.""" model: SvdModel + min_enum_members: int def process(self, elem: ElementTree.Element) -> None: """Process a single element.""" TAG_PROCESSORS[elem.tag](elem, self, getLogger(elem.tag)) @staticmethod - def svd(path: Path) -> "SvdProcessingTask": + def svd(path: Path, min_enum_members: int) -> "SvdProcessingTask": """Process a single SVD file.""" - task = SvdProcessingTask(SvdModel({})) + task = SvdProcessingTask(SvdModel({}), min_enum_members) task.process(ElementTree.parse(path).getroot()) return task @@ -60,7 +61,7 @@ def generate_configs(self, path: Path) -> None: for group in peripheral_groups(self.model.peripherals).values(): output_dir = path.joinpath(group.root.base_name()) output_dir.mkdir(exist_ok=True) - handle_group(output_dir, group, includes) + handle_group(output_dir, group, includes, self.min_enum_members) ARBITER.encode( path.joinpath("ifgen.yaml"), @@ -69,5 +70,13 @@ def generate_configs(self, path: Path) -> None: str(rel(x.resolve(), base=path)) for x in includes ), "namespace": [meta["device"]["name"]], + "struct": { + "stream": False, + "codec": False, + "methods": False, + "unit_test": False, + "identifier": False, + }, + "enum": {"use_map": False, "identifier": False}, }, ) diff --git a/local/variables/package.yaml b/local/variables/package.yaml index 7870108..b41344e 100644 --- a/local/variables/package.yaml +++ b/local/variables/package.yaml @@ -1,5 +1,5 @@ --- major: 3 -minor: 1 -patch: 7 +minor: 2 +patch: 0 entry: ig diff --git a/pyproject.toml b/pyproject.toml index a87745a..bb3500c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta:__legacy__" [project] name = "ifgen" -version = "3.1.7" +version = "3.2.0" description = "An interface generator for distributed computing." readme = "README.md" requires-python = ">=3.11" From ab6375ce0869ebe68716da16ef12bc4fdef158cd Mon Sep 17 00:00:00 2001 From: Vaughn Kottler Date: Thu, 19 Oct 2023 23:53:38 -0500 Subject: [PATCH 2/2] Use bit width for "min" enum checking --- ifgen/commands/svd.py | 8 ++++---- ifgen/svd/group/fields.py | 16 ++++++++-------- ifgen/svd/task.py | 8 ++++---- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/ifgen/commands/svd.py b/ifgen/commands/svd.py index ab6e081..86ee7e5 100644 --- a/ifgen/commands/svd.py +++ b/ifgen/commands/svd.py @@ -38,14 +38,14 @@ def svd_cmd(args: _Namespace) -> int: enums.PRUNE_ENUMS = enable_pruning base.PRUNE_STRUCTS = enable_pruning - SvdProcessingTask.svd(path, args.min_enum_members).generate_configs( + SvdProcessingTask.svd(path, args.min_enum_width).generate_configs( args.output ) return 0 -DEFAULT_MIN_ENUMS = 3 +DEFAULT_MIN_ENUM_WIDTH = 2 def add_svd_cmd(parser: _ArgumentParser) -> _CommandFunction: @@ -61,9 +61,9 @@ def add_svd_cmd(parser: _ArgumentParser) -> _CommandFunction: parser.add_argument( "-m", - "--min-enum_members", + "--min-enum-width", type=int, - default=DEFAULT_MIN_ENUMS, + default=DEFAULT_MIN_ENUM_WIDTH, help=( "minimum number of enumeration elements to warrant " "generating an enumeration definition (default: %(default)s)" diff --git a/ifgen/svd/group/fields.py b/ifgen/svd/group/fields.py index 8ba2727..4b887a4 100644 --- a/ifgen/svd/group/fields.py +++ b/ifgen/svd/group/fields.py @@ -95,7 +95,7 @@ def process_bit_fields( output: dict[str, Any], enums: EnumMap, peripheral: str, - min_enum_members: int, + min_enum_width: int, ) -> None: """Get bit-field declarations for a given register.""" @@ -115,7 +115,7 @@ def process_bit_fields( # Register enumeration. raw = translate_enums(field.enum) - if len(raw) >= min_enum_members: + if field_data["width"] >= min_enum_width: new_enum: dict[str, Any] = {"enum": raw} new_enum.update(ENUM_DEFAULTS) @@ -142,7 +142,7 @@ def handle_register( register_map: RegisterMap, enums: EnumMap, peripheral: str, - min_enum_members: int, + min_enum_width: int, ) -> tuple[int, StructField]: """Handle a register entry.""" @@ -175,7 +175,7 @@ def handle_register( register.handle_description(data, prefix=f"({', '.join(notes)}) ") # Handle bit fields. - process_bit_fields(register, data, enums, peripheral, min_enum_members) + process_bit_fields(register, data, enums, peripheral, min_enum_width) # Handle alternates. alts = register.alternates @@ -193,7 +193,7 @@ def handle_register( alt_data, enums, peripheral, - min_enum_members, + min_enum_width, ) # Forward array information (might be necessary at some point). @@ -212,7 +212,7 @@ def struct_fields( structs: StructMap, enums: EnumMap, peripheral: str, - min_enum_members: int, + min_enum_width: int, size: int = None, ) -> tuple[int, list[StructField]]: """Generate data for struct fields.""" @@ -230,10 +230,10 @@ def struct_fields( for item in registers: inst_size, field = ( - handle_cluster(item, structs, enums, peripheral, min_enum_members) + handle_cluster(item, structs, enums, peripheral, min_enum_width) if isinstance(item, Cluster) else handle_register( - item, register_map, enums, peripheral, min_enum_members + item, register_map, enums, peripheral, min_enum_width ) ) if inst_size > 0: diff --git a/ifgen/svd/task.py b/ifgen/svd/task.py index 492afb1..a2b271f 100644 --- a/ifgen/svd/task.py +++ b/ifgen/svd/task.py @@ -30,17 +30,17 @@ class SvdProcessingTask: """A container for SVD-processing state.""" model: SvdModel - min_enum_members: int + min_enum_width: int def process(self, elem: ElementTree.Element) -> None: """Process a single element.""" TAG_PROCESSORS[elem.tag](elem, self, getLogger(elem.tag)) @staticmethod - def svd(path: Path, min_enum_members: int) -> "SvdProcessingTask": + def svd(path: Path, min_enum_width: int) -> "SvdProcessingTask": """Process a single SVD file.""" - task = SvdProcessingTask(SvdModel({}), min_enum_members) + task = SvdProcessingTask(SvdModel({}), min_enum_width) task.process(ElementTree.parse(path).getroot()) return task @@ -61,7 +61,7 @@ def generate_configs(self, path: Path) -> None: for group in peripheral_groups(self.model.peripherals).values(): output_dir = path.joinpath(group.root.base_name()) output_dir.mkdir(exist_ok=True) - handle_group(output_dir, group, includes, self.min_enum_members) + handle_group(output_dir, group, includes, self.min_enum_width) ARBITER.encode( path.joinpath("ifgen.yaml"),