Skip to content

Commit

Permalink
Merge pull request #30 from vkottler/dev/2.4.1
Browse files Browse the repository at this point in the history
Initial output generation from SVD
  • Loading branch information
vkottler authored Oct 5, 2023
2 parents 50b5c65 + 7b81322 commit 16c8a71
Show file tree
Hide file tree
Showing 29 changed files with 527 additions and 170 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ jobs:
- run: |
mk python-release owner=vkottler \
repo=ifgen version=2.4.0
repo=ifgen version=2.4.1
if: |
matrix.python-version == '3.11'
&& matrix.system == 'ubuntu-latest'
Expand Down
2 changes: 1 addition & 1 deletion .isort.cfg
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[settings]
known_first_party=ifgen,vmklib
skip=tests/data
skip=tests/data,config
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
=====================================
generator=datazen
version=3.1.3
hash=6f2705d0caa7d536cae7a9eb581f7d7d
hash=fd06f3c891302b90329a9edaf282bd31
=====================================
-->

# ifgen ([2.4.0](https://pypi.org/project/ifgen/))
# ifgen ([2.4.1](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)
Expand Down
4 changes: 2 additions & 2 deletions ifgen/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# =====================================
# generator=datazen
# version=3.1.3
# hash=50538057667f048e4e1c2e1232e77e1b
# hash=f049d3e8785fc691ce45835642aa24c8
# =====================================

"""
Expand All @@ -10,4 +10,4 @@

DESCRIPTION = "An interface generator for distributed computing."
PKG_NAME = "ifgen"
VERSION = "2.4.0"
VERSION = "2.4.1"
14 changes: 10 additions & 4 deletions ifgen/commands/svd.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from argparse import ArgumentParser as _ArgumentParser
from argparse import Namespace as _Namespace
from logging import getLogger
from pathlib import Path

# third-party
from vcorelib.args import CommandFunction as _CommandFunction
Expand All @@ -30,17 +31,22 @@ def svd_cmd(args: _Namespace) -> int:
)
assert path is not None, args.svd_file

task = SvdProcessingTask.svd(path)

# generate output files etc. ?
assert task
SvdProcessingTask.svd(path).generate_configs(args.output)

return 0


def add_svd_cmd(parser: _ArgumentParser) -> _CommandFunction:
"""Add svd-command arguments to its parser."""

parser.add_argument(
"-o",
"--output",
type=Path,
default=f"{PKG_NAME}-out",
help="output directory for configuration files",
)

parser.add_argument(
"svd_file", type=str, help="path/uri to a CMSIS-SVD file"
)
Expand Down
1 change: 1 addition & 0 deletions ifgen/data/schemas/Struct.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ includes:
- has_description.yaml
- has_namespace.yaml
- has_json_indent.yaml
- has_expected_size.yaml

required: [fields]

Expand Down
1 change: 1 addition & 0 deletions ifgen/data/schemas/StructField.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
includes:
- has_description.yaml
- has_volatile.yaml
- has_expected_size.yaml

required: [name, type]

Expand Down
4 changes: 4 additions & 0 deletions ifgen/data/schemas/has_expected_size.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
properties:
expected_size:
type: integer
2 changes: 1 addition & 1 deletion ifgen/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
runtimepy>=2.1.1
vcorelib>=2.6.1
vcorelib>=3.0.0
26 changes: 24 additions & 2 deletions ifgen/struct/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ def struct_fields(task: GenerateTask, writer: IndentedFileWriter) -> None:
with writer.trailing_comment_lines(style=CommentStyle.C_DOXYGEN) as lines:
# Fields.
for field in task.instance["fields"]:
enforce_expected_size(
task.env.size(field["type"]),
field,
f"{task.name}.{field['name']}",
)
lines.append(struct_line(field["name"], field, field["volatile"]))

lines.append(("", None))
Expand Down Expand Up @@ -94,6 +99,20 @@ def struct_instance(
)


def enforce_expected_size(
size: int, data: dict[str, Any], assert_msg: str
) -> None:
"""Enforce an expected-size field."""

# If expected size is set, verify it.
if "expected_size" in data:
assert data["expected_size"] == size, (
assert_msg,
data["expected_size"],
size,
)


def create_struct(task: GenerateTask) -> None:
"""Create a header file based on a struct definition."""

Expand All @@ -115,10 +134,13 @@ def create_struct(task: GenerateTask) -> None:
f"{task.name}'s identifier.",
)
)

size = task.env.types.size(task.name)
enforce_expected_size(size, task.instance, task.name)

lines.append(
(
f"static constexpr std::size_t size = "
f"{task.env.types.size(task.name)};",
f"static constexpr std::size_t size = {size};",
f"{task.name}'s size in bytes.",
)
)
Expand Down
54 changes: 54 additions & 0 deletions ifgen/svd/group/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
"""
A module implementing interfaces for processing a group of peripherals.
"""

# built-in
from pathlib import Path
from typing import Any

# third-party
from vcorelib.io import ARBITER

# internal
from ifgen.svd.group.base import PeripheralGroup, peripheral_groups
from ifgen.svd.group.fields import DEFAULT_STRUCT, StructMap, struct_fields
from ifgen.svd.model.peripheral import Peripheral

__all__ = ["PeripheralGroup", "peripheral_groups", "handle_group"]


def struct_instance(peripheral: Peripheral) -> dict[str, Any]:
"""Get struct instance data."""

return {
"name": peripheral.name,
"address": peripheral.raw_data["baseAddress"],
}


def struct_data(group: PeripheralGroup, structs: StructMap) -> dict[str, Any]:
"""Get struct data for a peripheral group."""

data: dict[str, Any] = {}
peripheral = group.root
peripheral.handle_description(data)

data["instances"] = [struct_instance(x) for x in group.peripherals]
size, data["fields"] = struct_fields(peripheral.registers, structs)
data["expected_size"] = size

data.update(DEFAULT_STRUCT)
return data


def handle_group(
output_dir: Path, group: PeripheralGroup, includes: set[Path]
) -> None:
"""Handle a peripheral group."""

output = output_dir.joinpath("include.yaml")
includes.add(output)

structs: StructMap = {}
structs[group.root.base_name] = struct_data(group, structs)
ARBITER.encode(output, {"structs": structs})
49 changes: 49 additions & 0 deletions ifgen/svd/group/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
"""
A module implementing base interfaces for processing a group of peripherals.
"""

# built-in
from dataclasses import dataclass
from typing import Iterator

# internal
from ifgen.svd.model.peripheral import Peripheral


@dataclass
class PeripheralGroup:
"""A container for peripherals that have the same register layout."""

root: Peripheral
derivatives: list[Peripheral]

@property
def peripherals(self) -> Iterator[Peripheral]:
"""Get all peripheral instances."""
yield self.root
yield from self.derivatives


def peripheral_groups(
peripherals: dict[str, Peripheral]
) -> dict[str, PeripheralGroup]:
"""Organize peripherals into groups."""

result: dict[str, PeripheralGroup] = {}

for name, peripheral in peripherals.items():
if name not in result and not peripheral.derived:
# Validate this later.
result[name] = PeripheralGroup(None, []) # type: ignore

if peripheral.derived:
result[peripheral.derived_elem.name].derivatives.append(peripheral)
else:
assert result[name].root is None, result[name].root
result[name].root = peripheral

# Validate groups.
for name, group in result.items():
assert group.root is not None, (name, group)

return result
70 changes: 70 additions & 0 deletions ifgen/svd/group/fields.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
"""
A module for generating configuration data for struct fields.
"""

# built-in
from typing import Any

# internal
from ifgen.svd.model.peripheral import Cluster, Register, RegisterData

StructMap = dict[str, Any]
StructField = dict[str, Any]
DEFAULT_STRUCT = {"stream": False, "codec": False}


def handle_cluster(
cluster: Cluster, structs: StructMap
) -> tuple[int, StructField]:
"""Handle a cluster element."""

# 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)
cluster_struct["expected_size"] = size
cluster_struct.update(DEFAULT_STRUCT)

# compute expected size?

structs[cluster.name] = cluster_struct

# This needs to be an array element somehow. Use a namespace?
result: StructField = {"name": cluster.name, "expected_size": size}
cluster.handle_description(result)
return size, result


def handle_register(register: Register) -> tuple[int, StructField]:
"""Handle a register entry."""

# handle register is array + get size from peripheral if necessary
size = register.size
data = {
"name": register.name,
"type": register.c_type,
"expected_size": size,
}
register.handle_description(data)
return size, data


def struct_fields(
registers: RegisterData, structs: StructMap, size: int = None
) -> tuple[int, list[StructField]]:
"""Generate data for struct fields."""

fields = []

if size is None:
size = 0

for item in registers:
inst_size, field = (
handle_cluster(item, structs)
if isinstance(item, Cluster)
else handle_register(item)
)
fields.append(field)
size += inst_size

return size, fields
22 changes: 21 additions & 1 deletion ifgen/svd/model/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

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

# internal
Expand All @@ -21,6 +21,26 @@ class SvdModel:
device: Optional[Device] = None
cpu: Optional[Cpu] = None

def metadata(self) -> dict[str, Any]:
"""Get device and CPU metadata."""

result: dict[str, Any] = {}

if self.device is not None:
result["device"] = self.device.raw_data
if self.cpu is not None:
result["cpu"] = self.cpu.raw_data

for name, peripheral in self.peripherals.items():
result[name] = {
"interrupts": [x.raw_data for x in peripheral.interrupts],
"address_blocks": [
x.raw_data for x in peripheral.address_blocks
],
}

return result

def assign_device(self, device: Device) -> None:
"""Assign a device instance."""
assert self.device is None, self.device
Expand Down
Loading

0 comments on commit 16c8a71

Please sign in to comment.