Skip to content

Commit

Permalink
3.3.0 - Add more advanced SVD support
Browse files Browse the repository at this point in the history
  • Loading branch information
vkottler committed Jul 5, 2024
1 parent 0137dd7 commit d56af81
Show file tree
Hide file tree
Showing 20 changed files with 197 additions and 55 deletions.
16 changes: 10 additions & 6 deletions .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ on:
env:
TWINE_PASSWORD: ${{secrets.TWINE_PASSWORD}}
GITHUB_API_TOKEN: ${{secrets.API_TOKEN}}
CODECOV_TOKEN: ${{secrets.CODECOV_TOKEN}}

jobs:
build:
Expand All @@ -18,7 +19,6 @@ jobs:
strategy:
matrix:
python-version:
- "3.11"
- "3.12"
system:
- ubuntu-latest
Expand Down Expand Up @@ -57,29 +57,33 @@ jobs:

- run: mk docs
if: |
matrix.python-version == '3.11'
matrix.python-version == '3.12'
&& matrix.system == 'ubuntu-latest'
- run: mk python-test
env:
PY_TEST_EXTRA_ARGS: --cov-report=xml

- uses: codecov/codecov-action@v3
- uses: codecov/codecov-action@v3.1.5
with:
fail_ci_if_error: true
verbose: true
token: ${{secrets.CODECOV_TOKEN}}

- run: mk pypi-upload-ci
env:
TWINE_USERNAME: __token__
if: |
matrix.python-version == '3.11'
matrix.python-version == '3.12'
&& matrix.system == 'ubuntu-latest'
&& env.TWINE_PASSWORD != ''
&& github.ref_name == 'master'
- run: |
mk python-release owner=vkottler \
repo=ifgen version=3.2.1
repo=ifgen version=3.3.0
if: |
matrix.python-version == '3.11'
matrix.python-version == '3.12'
&& matrix.system == 'ubuntu-latest'
&& env.GITHUB_API_TOKEN != ''
&& github.ref_name == 'master'
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ coverage*.xml
tags
mklocal
docs
src
3 changes: 2 additions & 1 deletion .pylintrc
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[DESIGN]
max-args=8
max-args=9
max-attributes=8
max-locals=17

[MESSAGES CONTROL]
disable=duplicate-code
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2023 Vaughn Kottler
Copyright (c) 2024 Vaughn Kottler

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
=====================================
generator=datazen
version=3.1.4
hash=c432187b0744b91a3bf18c6fc28df5eb
hash=35493b15233479037424afb95e796e3e
=====================================
-->

# ifgen ([3.2.1](https://pypi.org/project/ifgen/))
# ifgen ([3.3.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)
Expand All @@ -29,7 +29,6 @@

This package is tested with the following Python minor versions:

* [`python3.11`](https://docs.python.org/3.11/)
* [`python3.12`](https://docs.python.org/3.12/)

## Platform Support
Expand Down
2 changes: 1 addition & 1 deletion config
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.4
# hash=302dd11b12a573508cf3316546a384ef
# hash=bf2c126f5c469e9b0483e934ee695f9e
# =====================================

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

DESCRIPTION = "An interface generator for distributed computing."
PKG_NAME = "ifgen"
VERSION = "3.2.1"
VERSION = "3.3.0"
16 changes: 14 additions & 2 deletions ifgen/commands/gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
# built-in
from argparse import ArgumentParser as _ArgumentParser
from argparse import Namespace as _Namespace
import sys

# third-party
from vcorelib.args import CommandFunction as _CommandFunction
Expand All @@ -22,6 +23,8 @@ def gen_cmd(args: _Namespace) -> int:

root = normalize(args.root)

sys.setrecursionlimit(args.recursion)

generate(root.resolve(), load(combine_if_not_absolute(root, args.config)))

return 0
Expand All @@ -30,16 +33,25 @@ def gen_cmd(args: _Namespace) -> int:
def add_gen_cmd(parser: _ArgumentParser) -> _CommandFunction:
"""Add gen-command arguments to its parser."""

parser.add_argument(
"--recursion",
type=int,
default=10000,
help="recursion limit to set (default: '%(default)s')",
)
parser.add_argument(
"-c",
"--config",
default=f"{PKG_NAME}.yaml",
help="configuration file to use",
help="configuration file to use (default: '%(default)s')",
)
parser.add_argument(
"-r",
"--root",
default=".",
help="root directory to use for relative paths",
help=(
"root directory to use for relative "
"paths (default: '%(default)s')"
),
)
return gen_cmd
2 changes: 1 addition & 1 deletion ifgen/environment/field.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def process_field(
difference = expected - size

assert difference >= 0, (
f"({struct_name}.{field_name}) current={size} "
f"{difference} ({struct_name}.{field_name}) current={size} "
f"!= expected={expected}"
)

Expand Down
4 changes: 2 additions & 2 deletions ifgen/svd/group/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ def translate_enums(enum: EnumeratedValues) -> EnumValues:
enum_data: dict[str, Any] = {}
value.handle_description(enum_data)

value_str: str = value.raw_data["value"]
value_str: str = value.raw_data["value"].lower()

prefix = ""
for possible_prefix in ("#", "0b", "0x"):
Expand All @@ -154,7 +154,7 @@ def translate_enums(enum: EnumeratedValues) -> EnumValues:

if prefix in ("#", "0b"):
enum_data["value"] = int(
value_str[len(prefix) :].replace("X", "1"), 2
value_str[len(prefix) :].replace("x", "1"), 2
)
elif prefix == "0x":
enum_data["value"] = int(value_str[len(prefix) :], 16)
Expand Down
125 changes: 107 additions & 18 deletions ifgen/svd/group/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,16 @@
"""

# built-in
from typing import Any, Iterable
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
from ifgen.svd.model.peripheral import (
Cluster,
Register,
RegisterData,
register_groups,
)

StructMap = dict[str, Any]
StructField = dict[str, Any]
Expand Down Expand Up @@ -58,27 +63,30 @@ def handle_cluster(
cluster.children, structs, enums, peripheral, min_enum_members
)

# Too difficult due to padding (may need to comment out).
cluster_struct["expected_size"] = size
# Too difficult due to padding (may need to comment out). Padding within
# a cluster isn't currently handled.
# cluster_struct["expected_size"] = size

cluster_struct.update(DEFAULT_STRUCT)

raw_name = cluster.name.replace("[%s]", "")
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),
# "expected_size": size,
# "expected_offset": parse_offset(cluster.raw_data),
}
if array_dim > 1:
result["array_length"] = array_dim
Expand Down Expand Up @@ -125,7 +133,7 @@ def process_bit_fields(

# Check if enum is unique.
enum_name = get_enum_name(
f"{peripheral}_{register.name}_{name}".replace("[%s]", ""),
sanitize_name(f"{peripheral}_{register.name}_{name}"),
peripheral,
raw,
)
Expand All @@ -137,6 +145,12 @@ def process_bit_fields(
output["fields"] = result


def sanitize_name(name: str) -> str:
"""Remove special characters from a name."""

return name.replace("[%s]", "").replace("%s", "")


def handle_register(
register: Register,
register_map: RegisterMap,
Expand All @@ -151,14 +165,15 @@ def handle_register(
if "alternateRegister" in register.raw_data:
return 0, {}

# Ensure that a correct result will be produced.
check_not_handled_fields(register.raw_data, ["alternateGroup"])
# 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": register.name.replace("[%s]", ""),
"name": sanitize_name(register.name),
"type": register.c_type,
"expected_size": size,
"expected_offset": parse_offset(register.raw_data),
Expand Down Expand Up @@ -207,6 +222,68 @@ def handle_register(
return size, data


def handle_register_group(
name: str,
registers: list[Register],
register_map: RegisterMap,
enums: EnumMap,
peripheral: str,
min_enum_width: int,
) -> tuple[int, StructField]:
"""TODO."""

# 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
)


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


def struct_fields(
registers: RegisterData,
structs: StructMap,
Expand All @@ -228,15 +305,27 @@ def struct_fields(
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_cluster(item, structs, enums, peripheral, min_enum_width)
if isinstance(item, Cluster)
else handle_register(
item, register_map, enums, peripheral, min_enum_width
)
inst_size, field = handle_item(
item,
structs,
enums,
peripheral,
min_enum_width,
groups_handled,
register_map,
groups,
by_name,
)
if inst_size > 0:

if inst_size > 0 and field is not None:
fields.append(field)
size += inst_size

Expand Down
Loading

0 comments on commit d56af81

Please sign in to comment.