Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

config: Support MBED_ROM_START and friends #270

Merged
merged 2 commits into from
Apr 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions news/222.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add support for MBED_ROM_START, MBED_ROM_SIZE, MBED_RAM_START and MBED_RAM_SIZE in config system.
21 changes: 19 additions & 2 deletions src/mbed_tools/build/_internal/config/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from collections import UserDict
from typing import Any, Iterable, Hashable, Callable, List

from mbed_tools.build._internal.config.source import Override, ConfigSetting
from mbed_tools.build._internal.config.source import Memory, Override, ConfigSetting

logger = logging.getLogger(__name__)

Expand All @@ -18,13 +18,15 @@ class Config(UserDict):

This object understands how to populate the different 'config sections' which all have different rules for how the
settings are collected.
Applies overrides, appends macros and updates config settings.
Applies overrides, appends macros, updates memories, and updates config settings.
"""

def __setitem__(self, key: Hashable, item: Any) -> None:
"""Set an item based on its key."""
if key == CONFIG_SECTION:
self._update_config_section(item)
elif key == MEMORIES_SECTION:
self._update_memories_section(item)
elif key == OVERRIDES_SECTION:
self._handle_overrides(item)
elif key == MACROS_SECTION:
Expand Down Expand Up @@ -67,6 +69,20 @@ def _update_config_section(self, config_settings: List[ConfigSetting]) -> None:

self.data[CONFIG_SECTION] = self.data.get(CONFIG_SECTION, []) + config_settings

def _update_memories_section(self, memories: List[Memory]) -> None:
defined_memories = self.data.get(MEMORIES_SECTION, [])
for memory in memories:
logger.debug(f"Adding memory settings `{memory.name}: start={memory.start} size={memory.size}`")
prev_defined = next((mem for mem in defined_memories if mem.name == memory.name), None)
if prev_defined is None:
rwalton-arm marked this conversation as resolved.
Show resolved Hide resolved
rwalton-arm marked this conversation as resolved.
Show resolved Hide resolved
defined_memories.append(memory)
else:
logger.warning(
f"You are attempting to redefine `{memory.name}` from {prev_defined.namespace}.\n"
f"The values from `{memory.namespace}` will be ignored"
)
self.data[MEMORIES_SECTION] = defined_memories

def _find_first_config_setting(self, predicate: Callable) -> Any:
"""Find first config setting based on `predicate`.

Expand All @@ -89,6 +105,7 @@ def _find_first_config_setting(self, predicate: Callable) -> Any:

CONFIG_SECTION = "config"
MACROS_SECTION = "macros"
MEMORIES_SECTION = "memories"
OVERRIDES_SECTION = "overrides"


Expand Down
55 changes: 53 additions & 2 deletions src/mbed_tools/build/_internal/config/source.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ def prepare(
) -> dict:
"""Prepare a config source for entry into the Config object.

Extracts config and override settings from the source. Flattens these nested dictionaries out into lists of
objects which are namespaced in the way the Mbed config system expects.
Extracts memory, config and override settings from the source. Flattens these nested dictionaries out into
lists of objects which are namespaced in the way the Mbed config system expects.

Args:
input_data: The raw config JSON object parsed from the config file.
Expand All @@ -46,6 +46,11 @@ def prepare(
for key in data:
data[key] = _sanitise_value(data[key])

memories = _extract_memories(namespace, data)

if memories:
data["memories"] = memories

if "config" in data:
data["config"] = _extract_config_settings(namespace, data["config"])

Expand Down Expand Up @@ -78,6 +83,31 @@ def __post_init__(self) -> None:
self.value = _sanitise_value(self.value)


@dataclass
class Memory:
"""Representation of a defined RAM/ROM region."""

name: str
namespace: str
start: str
size: str

def __post_init__(self) -> None:
"""Convert start and size to hex format strings."""
try:
self.start = hex(int(self.start, 0))
except ValueError:
raise ValueError(
f"Value of MBED_{self.name}_START in {self.namespace}, {self.start} is invalid: must be an integer"
)
try:
self.size = hex(int(self.size, 0))
except ValueError:
raise ValueError(
f"Value of MBED_{self.name}_SIZE in {self.namespace}, {self.size} is invalid: must be an integer"
)


@dataclass
class Override:
"""Representation of a config override.
Expand Down Expand Up @@ -128,6 +158,27 @@ def _extract_config_settings(namespace: str, config_data: dict) -> List[ConfigSe
return settings


def _extract_memories(namespace: str, data: dict) -> List[Memory]:
memories = []
for mem in ["rom", "ram"]:
start_attr = f"mbed_{mem}_start"
size_attr = f"mbed_{mem}_size"
start = data.get(start_attr)
size = data.get(size_attr)

if size is not None and start is not None:
logger.debug(f"Extracting MBED_{mem.upper()} definitions in {namespace}: _START={start}, _SIZE={size}.")

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The line spacing here seems a little off. We should probably remove this blank line.

memory = Memory(mem.upper(), namespace, start, size)
memories.append(memory)
elif start is not None or size is not None:
raise ValueError(
f"{size_attr.upper()} and {start_attr.upper()} must be defined together. Only "
f"{'START' if start is not None else 'SIZE'} is defined in the lib {namespace}."
)
return memories


def _extract_target_overrides(
namespace: str, override_data: dict, allowed_target_labels: Iterable[str]
) -> List[Override]:
Expand Down
4 changes: 4 additions & 0 deletions src/mbed_tools/build/_internal/templates/mbed_config.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ set(MBED_CONFIG_DEFINITIONS
"-D{{setting_name}}={{value}}"
{% endif -%}
{%- endfor -%}
{% for memory in memories %}
"-DMBED_{{memory.name}}_START={{memory.start}}"
"-DMBED_{{memory.name}}_SIZE={{memory.size}}"
{%- endfor -%}
{% for macro in macros %}
"{{macro|replace("\"", "\\\"")}}"
{%- endfor %}
Expand Down
14 changes: 13 additions & 1 deletion tests/build/_internal/config/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
# Copyright (c) 2020-2021 Arm Limited and Contributors. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
import logging
import pytest

from mbed_tools.build._internal.config.config import Config
from mbed_tools.build._internal.config.source import prepare, ConfigSetting, Override
from mbed_tools.build._internal.config.source import prepare, ConfigSetting, Memory, Override


class TestConfig:
Expand All @@ -24,6 +25,17 @@ def test_raises_when_trying_to_add_duplicate_config_setting(self):
with pytest.raises(ValueError, match="lib.param already defined"):
conf.update(prepare({"config": {"param": {"value": 0}}}, source_name="lib"))

def test_logs_ignore_mbed_ram_repeated(self, caplog):
caplog.set_level(logging.DEBUG)
input_dict = {"mbed_ram_size": "0x80000", "mbed_ram_start": "0x24000000"}
input_dict2 = {"mbed_ram_size": "0x78000", "mbed_ram_start": "0x24200000"}

conf = Config(prepare(input_dict, source_name="lib1"))
conf.update(prepare(input_dict2, source_name="lib2"))

assert "values from `lib2` will be ignored" in caplog.text
assert conf["memories"] == [Memory("RAM", "lib1", "0x24000000", "0x80000")]

def test_target_overrides_handled(self):
conf = Config(
{
Expand Down
49 changes: 48 additions & 1 deletion tests/build/_internal/config/test_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
# Copyright (c) 2020-2021 Arm Limited and Contributors. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
import pytest

from mbed_tools.build._internal.config import source
from mbed_tools.build._internal.config.source import Override
from mbed_tools.build._internal.config.source import Memory, Override


class TestPrepareSource:
Expand Down Expand Up @@ -118,3 +120,48 @@ def test_converts_config_setting_value_lists_to_sets(self):
assert conf["config"][0].value == {"ETHERNET", "WIFI"}
assert conf["sectors"] == {0, 2048}
assert conf["header_info"] == {0, 2048, "bobbins", "magic"}

def test_memory_attr_extracted(self):
lib = {
"mbed_ram_size": "0x80000",
"mbed_ram_start": "0x24000000",
"mbed_rom_size": "0x200000",
"mbed_rom_start": "0x08000000",
}

conf = source.prepare(lib, "lib")

assert Memory("RAM", "lib", "0x24000000", "0x80000") in conf["memories"]
assert Memory("ROM", "lib", "0x8000000", "0x200000") in conf["memories"]

def test_memory_attr_converted_as_hex(self):
input_dict = {"mbed_ram_size": "1024", "mbed_ram_start": "0x24000000"}

conf = source.prepare(input_dict, source_name="lib")

memory, *_ = conf["memories"]
assert memory.size == "0x400"

def test_raises_memory_size_not_integer(self):
input_dict = {"mbed_ram_size": "NOT INT", "mbed_ram_start": "0x24000000"}

with pytest.raises(ValueError, match="_SIZE in lib, NOT INT is invalid: must be an integer"):
source.prepare(input_dict, "lib")

def test_raises_memory_start_not_integer(self):
input_dict = {"mbed_ram_size": "0x80000", "mbed_ram_start": "NOT INT"}

with pytest.raises(ValueError, match="_START in lib, NOT INT is invalid: must be an integer"):
source.prepare(input_dict, "lib")

def test_raises_memory_size_defined_not_start(self):
input_dict = {"mbed_ram_size": "0x80000"}

with pytest.raises(ValueError, match="Only SIZE is defined"):
source.prepare(input_dict)

def test_raises_memory_start_defined_not_size(self):
input_dict = {"mbed_ram_start": "0x24000000"}

with pytest.raises(ValueError, match="Only START is defined"):
source.prepare(input_dict)