Adding NVM functionality to composition_yaml

This change does not include generating RTE calls,
it only updates the yaml2arxml input.

Change-Id: I67f32d3c31dccf0b290c6e5c934d71e27b1b439d
This commit is contained in:
Henrik Wahlqvist 2024-10-24 12:31:48 +02:00
parent 816553f1bc
commit b55335a3a4
13 changed files with 290 additions and 23 deletions

@ -373,7 +373,7 @@ def generate_dummy_var(build_cfg, unit_cfg, signal_if, udt):
LOG.info("Finished generating VcDummy (in %4.2f s)", time.time() - start_time)
def generate_nvm_def(build_cfg, unit_cfg, no_nvm_a2l):
def generate_nvm_def(nvm_def, no_nvm_a2l):
"""Generate the c&h-files which declares the NVM-ram.
The NVM-ram is declared in a struct per datatype length, in order
@ -382,15 +382,12 @@ def generate_nvm_def(build_cfg, unit_cfg, no_nvm_a2l):
created to minimize the needed model changes for access to the memory.
Args:
build_cfg (BuildProjConfig): Build project class holding where files should be stored.
unit_cfg (UnitConfigs): class holding units definitions, and which units to include.
nvm_def (NVMDef): Class holding the NVM definitions for the build.
no_nvm_a2l (bool): Do not generate A2L for NVM structs.
"""
LOG.info("******************************************************")
LOG.info("Start generating NVMDefinitions")
start_time = time.time()
tot_vars_nvm = unit_cfg.get_per_cfg_unit_cfg().get("nvm", {})
nvm_def = NVMDef(build_cfg, unit_cfg, tot_vars_nvm)
nvm_def.generate_nvm_config_files(no_nvm_a2l)
LOG.info(
"Finished generating NVMDefinitions (in %4.2f s)", time.time() - start_time
@ -777,7 +774,9 @@ def build(
udt = UserDefinedTypes(build_cfg, unit_cfg)
udt.generate_common_header_files()
generate_nvm_def(build_cfg, unit_cfg, no_nvm_a2l)
tot_vars_nvm = unit_cfg.get_per_cfg_unit_cfg().get("nvm", {})
nvm_def = NVMDef(build_cfg, unit_cfg, tot_vars_nvm)
generate_nvm_def(nvm_def, no_nvm_a2l)
start_time = time.time()
cnf_header = pjoin(src_dst_dir, build_cfg.get_feature_conf_header_name())
@ -904,7 +903,7 @@ def build(
zc_dids = ZCDIDs(build_cfg, unit_cfg)
axis_data = merged_a2l.get_characteristic_axis_data()
composition_yaml = CompositionYaml(
build_cfg, signal_if.composition_spec, unit_cfg, zc_core, zc_dids, axis_data
build_cfg, signal_if.composition_spec, unit_cfg, zc_core, zc_dids, nvm_def, axis_data
)
LOG.info("******************************************************")
composition_yaml.generate_yaml()

@ -263,6 +263,7 @@ class BuildProjConfig:
'includeStatic': file_config.get('includeStatic', True),
'includeShared': file_config.get('includeShared', True),
'includeDiagnostics': file_config.get('includeDiagnostics', True),
'includeNvm': file_config.get('includeNvm', True),
}
compositionName = file_config.get("compositionName", None)
if compositionName is not None:

@ -142,7 +142,7 @@ class ZCAL(BaseApplication):
raw = self.read_translation_files(definition)
self.composition_spec = {
key: raw[key] for key in ("port_interfaces", "data_types", "calls", "diagnostics") if key in raw
key: raw[key] for key in ("port_interfaces", "data_types", "calls", "diagnostics", "nv-needs") if key in raw
}
ports_info = {}
for port_name, port in raw.get("ports", {}).items():

@ -15,7 +15,7 @@ from powertrain_build.zone_controller.calibration import ZoneControllerCalibrati
class CompositionYaml(ProblemLogger):
"""Class for handling ZoneController composition yaml generation."""
def __init__(self, build_cfg, composition_spec, unit_cfg, zc_core, zc_dids, a2l_axis_data):
def __init__(self, build_cfg, composition_spec, unit_cfg, zc_core, zc_dids, nvm_def, a2l_axis_data):
"""Init.
Args:
@ -24,6 +24,7 @@ class CompositionYaml(ProblemLogger):
unit_cfg (UnitConfig): Object with unit configurations.
zc_core (ZCCore): Object with zone controller diagnositic event information.
zc_dids (ZCDIDs): Object with zone controller diagnostic DID information.
nvm_def (NVMDef): Object with NVM definition information.
a2l_axis_data (dict): Dict with characteristic axis data from A2L file.
"""
self.tl_to_autosar_base_types = {
@ -42,6 +43,7 @@ class CompositionYaml(ProblemLogger):
self.unit_cfg = unit_cfg
self.zc_core = zc_core
self.zc_dids = zc_dids
self.nvm_def = nvm_def
self.a2l_axis_data = a2l_axis_data
base_data_types = self.get_base_data_types() # Might not be necessary in the long run
self.data_types = {
@ -304,7 +306,7 @@ class CompositionYaml(ProblemLogger):
NOTE: This function sets the valid_dids property of the ZCDIDs object.
Returns:
(dict): Dict containing diagnostic information.
diag_dict (dict): Dict containing diagnostic information.
"""
diag_dict = {}
diagnostics = self.composition_spec.get("diagnostics", {})
@ -321,6 +323,82 @@ class CompositionYaml(ProblemLogger):
self.warning("Will not generate code for RIDs, add manually.")
return diag_dict
def _get_nvm_info(self):
"""Creates a dict with NVM information.
Returns:
nvm_dict (dict): Dict containing NVM information.
"""
prefix = self.build_cfg.get_scheduler_prefix()
yaml_nvm_definitions = self.composition_spec.get("nv-needs", {})
project_nvm_definitions = {
f"{prefix}{item['name']}": item for item in self.nvm_def.nvm_definitions
}
nvms_not_in_project = set(yaml_nvm_definitions.keys()) - set(project_nvm_definitions.keys())
nvms_not_in_yaml = set(project_nvm_definitions.keys()) - set(yaml_nvm_definitions.keys())
for key in nvms_not_in_project:
self.warning(f'Ignoring NVM definition {key} since it does not appear in nvm_structs.json.')
del yaml_nvm_definitions[key]
for key in nvms_not_in_yaml:
self.warning(f'Ignoring NVM definition {key} since it does not appear in the project NVM yaml file.')
data_types = {}
for nvm_name, nvm_data in yaml_nvm_definitions.items():
data_type_name = f"dt_{nvm_name}"
nr_of_unused_signals = project_nvm_definitions[nvm_name]["size"]
nvm_data.update({
"datatype": data_type_name,
"init": []
})
data_types[data_type_name] = {
"type": "RECORD",
"elements": {},
}
for signal in project_nvm_definitions[nvm_name]["signals"]:
nr_of_unused_signals -= signal["x_size"] * signal["y_size"]
size = max(signal["x_size"], 1) * max(signal["y_size"], 1)
if size > 1:
x_data_type_name = f"dt_{signal['name']}_x"
y_data_type_name = f"dt_{signal['name']}_y"
if signal["x_size"] > 1 and signal["y_size"] == 1:
nvm_data["init"].append([0] * signal["x_size"])
data_types[data_type_name]["elements"][signal["name"]] = x_data_type_name
data_types[x_data_type_name] = {
"type": "ARRAY",
"size": signal["x_size"],
"element": signal["type"],
}
elif signal["x_size"] > 1 and signal["y_size"] > 1:
nvm_data["init"].append([[0] * signal["y_size"]] * signal["x_size"])
data_types[data_type_name]["elements"][signal["name"]] = y_data_type_name
data_types[y_data_type_name] = {
"type": "ARRAY",
"size": signal["y_size"],
"element": x_data_type_name,
}
data_types[x_data_type_name] = {
"type": "ARRAY",
"size": signal["x_size"],
"element": signal["type"],
}
else:
self.critical("NVM signal size incorrect. x_size should not be 1 if y_size > 1.")
else:
nvm_data["init"].append(0)
data_types[data_type_name]["elements"][signal["name"]] = signal["type"]
if nr_of_unused_signals > 0:
nvm_data["init"].append([0] * nr_of_unused_signals)
data_types[data_type_name]["elements"]["unused"] = f"{data_type_name}_Unused"
data_types[f"{data_type_name}_Unused"] = {
"type": "ARRAY",
"size": nr_of_unused_signals,
"element": project_nvm_definitions[nvm_name]["default_datatype"],
}
return yaml_nvm_definitions, data_types
def _get_ports_info(self):
"""Creates a dict containing port information.
@ -413,10 +491,7 @@ class CompositionYaml(ProblemLogger):
software_component_name = self.build_cfg.get_composition_config("softwareComponentName")
software_component_template = self.build_cfg.get_composition_config("softwareComponentTemplate")
software_component_base = self.build_cfg.get_composition_config("softwareComponentBase")
data_types = {
**self.cal_class_info["autosar"]["data_types"],
**self.meas_class_info["autosar"]["data_types"],
}
swcs = {software_component_name: {}}
swcs[software_component_name]["type"] = "SWC" # Other types than swc??
if software_component_template is not None:
@ -432,6 +507,19 @@ class CompositionYaml(ProblemLogger):
diagnostic_info = self._get_diagnostic_info()
if self.build_cfg.get_composition_config("includeDiagnostics"):
swcs[software_component_name]["diagnostics"] = diagnostic_info
nvm_info, nvm_data_types_tmp = self._get_nvm_info()
if self.build_cfg.get_composition_config("includeNvm"):
swcs[software_component_name]["nv-needs"] = nvm_info
nvm_data_types = nvm_data_types_tmp
else:
nvm_data_types = {}
data_types = {
**self.cal_class_info["autosar"]["data_types"],
**self.meas_class_info["autosar"]["data_types"],
**nvm_data_types,
}
return swcs, data_types
def _get_variables(self):

@ -23,6 +23,7 @@ expected_result = {
},
},
"diagnostics": {},
"nv-needs": {},
"static": composition_yaml_setup.base_static,
"shared": composition_yaml_setup.base_shared,
"ports": {
@ -53,6 +54,7 @@ expected_custom_names_result = {
},
},
"diagnostics": {},
"nv-needs": {},
"static": composition_yaml_setup.base_static,
"shared": composition_yaml_setup.base_shared,
"ports": {
@ -88,6 +90,7 @@ expected_cal_result = {
}
},
"diagnostics": {},
"nv-needs": {},
"static": composition_yaml_setup.base_static,
"shared": composition_yaml_setup.cal_shared,
"ports": {

@ -176,6 +176,7 @@ expected_result = {
}
},
"diagnostics": {},
"nv-needs": {},
"static": composition_yaml_setup.base_static,
"shared": {
"tVcGpaDemo_X_DummyOne_x": {

@ -29,6 +29,7 @@ expected_result = {
}
},
"diagnostics": {},
"nv-needs": {},
"static": composition_yaml_setup.base_static,
"shared": composition_yaml_setup.base_shared,
"ports": {

@ -28,6 +28,7 @@ expected_result = {
}
},
"diagnostics": {},
"nv-needs": {},
"static": composition_yaml_setup.base_static,
"shared": composition_yaml_setup.base_shared,
"ports": {

@ -85,6 +85,7 @@ expected_result = {
},
}
},
"nv-needs": {},
"static": composition_yaml_setup.base_static,
"shared": composition_yaml_setup.base_shared,
"ports": {

@ -67,6 +67,7 @@ expected_result = {
},
},
},
"nv-needs": {},
"static": composition_yaml_setup.base_static,
"shared": composition_yaml_setup.base_shared,
"ports": {

@ -0,0 +1,148 @@
# Copyright 2024 Volvo Car Corporation
# Licensed under Apache 2.0.
"""Unit test data for powertrain_build.zone_controller.composition_yaml."""
from test_data.zone_controller.test_composition_yaml import composition_yaml_setup
project_nvm_definitions = [
{
"name": "NVM_LIST_8",
"allowed_datatypes": [
"Bool",
"UInt8",
"Int8"
],
"size": 8,
"instanceName": "nvm_list_8",
"default_datatype": "UInt8",
"includeStop": "",
"includeStart": "",
"persistent": "False",
"signals": [
{
"name": "sVcModelName_X_DummyNvmSignal",
"type": "UInt8",
"x_size": 1,
"y_size": 1
},
{
"name": "sVcModelName_X_DummyNvmSignal2",
"type": "UInt8",
"x_size": 2,
"y_size": 1
},
{
"name": "sVcModelName_X_DummyNvmSignal3",
"type": "UInt8",
"x_size": 2,
"y_size": 2
}
]
},
{
"name": "NVM_LIST_16",
"allowed_datatypes": [
"Float32",
"UInt32",
"Int32"
],
"size": 3,
"instanceName": "nvm_list_16",
"default_datatype": "UInt16",
"includeStop": "",
"includeStart": "",
"persistent": "False",
"signals": []
}
]
yaml_nvm_definitions = {
"prefix_NVM_LIST_8": {
"type": "type1",
"method": "DIRECT-CALL",
"runnables": ["Dummy"],
"attributes": {
"resistant-to-change": True,
"random-attribute": "Dummy",
},
},
}
additional_data_types = {
"dt_prefix_NVM_LIST_8": {
"type": "RECORD",
"elements": {
"sVcModelName_X_DummyNvmSignal": "UInt8",
"sVcModelName_X_DummyNvmSignal2": "dt_sVcModelName_X_DummyNvmSignal2_x",
"sVcModelName_X_DummyNvmSignal3": "dt_sVcModelName_X_DummyNvmSignal3_y",
"unused": "dt_prefix_NVM_LIST_8_Unused",
},
},
"dt_sVcModelName_X_DummyNvmSignal2_x": {
"size": 2,
"type": "ARRAY",
"element": "UInt8",
},
"dt_sVcModelName_X_DummyNvmSignal3_y": {
"size": 2,
"type": "ARRAY",
"element": "dt_sVcModelName_X_DummyNvmSignal3_x",
},
"dt_sVcModelName_X_DummyNvmSignal3_x": {
"size": 2,
"type": "ARRAY",
"element": "UInt8",
},
"dt_prefix_NVM_LIST_8_Unused": {
"size": 1,
"type": "ARRAY",
"element": "UInt8",
},
}
expected_result = {
"SoftwareComponents": {
"testName_SC": {
"type": "SWC",
"template": "ARTCSC",
"swcbase": "QM",
"runnables": {
"AR_prefix_VcExtINI": {
"type": "INIT",
"accesses": composition_yaml_setup.base_accesses
},
"AR_prefix_testRunnable": {
"period": 10,
"type": "PERIODIC",
"accesses": composition_yaml_setup.base_accesses
},
},
"diagnostics": {},
"nv-needs": {
"prefix_NVM_LIST_8": {
"type": "type1",
"method": "DIRECT-CALL",
"runnables": ["Dummy"],
"datatype": "dt_prefix_NVM_LIST_8",
"init": [0, [0, 0], [[0, 0], [0, 0]], [0]],
"attributes": {
"resistant-to-change": True,
"random-attribute": "Dummy",
},
},
},
"static": composition_yaml_setup.base_static,
"shared": composition_yaml_setup.base_shared,
"ports": {
"GlobSignNme": {"direction": "IN", "interface": "PIGlobSignNme"}
},
}
},
"DataTypes": {
**composition_yaml_setup.base_data_types,
**additional_data_types,
},
"PortInterfaces": composition_yaml_setup.base_port_interfaces,
"ExternalFiles": composition_yaml_setup.base_configuration
}

@ -11,6 +11,7 @@ from pathlib import Path
from powertrain_build import build_defs
from powertrain_build.lib import helper_functions
from powertrain_build.nvm_def import NVMDef
from powertrain_build.problem_logger import ProblemLogger
from powertrain_build.build_proj_config import BuildProjConfig
from powertrain_build.unit_configs import UnitConfigs
@ -222,6 +223,8 @@ class TestBuild(unittest.TestCase):
"""Check that NVM files are generated."""
unit_cfg = MagicMock(spec_set=UnitConfigs)
type(unit_cfg).base_types_headers = ''
tot_vars_nvm = unit_cfg.get_per_cfg_unit_cfg().get("nvm", {})
nvm_def = NVMDef(self.build_cfg, unit_cfg, tot_vars_nvm)
no_nvm_a2l = False
filepath = str(Path(SRC_DIR, 'output'))
files = [
@ -229,7 +232,7 @@ class TestBuild(unittest.TestCase):
filepath + '/vcc_nvm_struct.h',
filepath + '/vcc_nvm_struct.a2l']
remove(*files)
build.generate_nvm_def(self.build_cfg, unit_cfg, no_nvm_a2l)
build.generate_nvm_def(nvm_def, no_nvm_a2l)
self.build_cfg.get_src_code_dst_dir.assert_called()
unit_cfg.get_per_cfg_unit_cfg.assert_called()
exists(*files)

@ -22,6 +22,7 @@ from test_data.zone_controller.test_composition_yaml import (
composition_yaml_with_calls_no_optional_fields,
composition_yaml_with_dids,
composition_yaml_with_dtcs,
composition_yaml_with_nvm,
)
SRC_DIR = Path(__file__).parent
@ -47,6 +48,7 @@ def mock_get_composition_config_default(key):
'includeStatic': True,
'includeShared': True,
'includeDiagnostics': True,
'includeNvm': True,
}[key]
@ -65,6 +67,7 @@ def mock_get_composition_config_custom_names(key):
'includeStatic': True,
'includeShared': True,
'includeDiagnostics': True,
'includeNvm': True,
}[key]
@ -98,6 +101,8 @@ class TestCompositionYaml(unittest.TestCase):
self.zc_spec = copy.deepcopy(composition_yaml_setup.zc_spec)
self.nvm_def = MagicMock()
self.calibration_definitions = copy.deepcopy(composition_yaml_setup.calibration_definitions)
with patch.object(
@ -106,7 +111,7 @@ class TestCompositionYaml(unittest.TestCase):
return_value=self.calibration_definitions
):
self.composition_yaml = CompositionYaml(
self.build_cfg, self.zc_spec, self.unit_cfg, self.zc_core, self.zc_dids, {}
self.build_cfg, self.zc_spec, self.unit_cfg, self.zc_core, self.zc_dids, self.nvm_def, {}
)
def test_check_unsupported_fields(self):
@ -135,7 +140,7 @@ class TestCompositionYaml(unittest.TestCase):
"""Checking that the dict is generated correctly with custom names."""
self.build_cfg.get_composition_config.side_effect = mock_get_composition_config_custom_names
self.composition_yaml = CompositionYaml(
self.build_cfg, self.zc_spec, self.unit_cfg, self.zc_core, self.zc_dids, {}
self.build_cfg, self.zc_spec, self.unit_cfg, self.zc_core, self.zc_dids, self.nvm_def, {}
)
result = self.composition_yaml.gather_yaml_info()
self.assertDictEqual(composition_yaml.expected_custom_names_result, result)
@ -150,7 +155,7 @@ class TestCompositionYaml(unittest.TestCase):
return_value=self.calibration_definitions
):
self.composition_yaml = CompositionYaml(
self.build_cfg, self.zc_spec, self.unit_cfg, self.zc_core, self.zc_dids, {}
self.build_cfg, self.zc_spec, self.unit_cfg, self.zc_core, self.zc_dids, self.nvm_def, {}
)
result = self.composition_yaml.gather_yaml_info()
self.assertDictEqual(composition_yaml.expected_cal_result, result)
@ -164,7 +169,7 @@ class TestCompositionYaml(unittest.TestCase):
self.calibration_definitions + composition_yaml_with_a2l_axis_data.calibration_definitions
with patch.object(CompositionYaml, "_get_all_calibration_definitions", return_value=calibration_definitions):
self.composition_yaml = CompositionYaml(
self.build_cfg, self.zc_spec, self.unit_cfg, self.zc_core, self.zc_dids, a2l_axis_data
self.build_cfg, self.zc_spec, self.unit_cfg, self.zc_core, self.zc_dids, self.nvm_def, a2l_axis_data
)
result = self.composition_yaml.gather_yaml_info()
@ -186,7 +191,7 @@ class TestCompositionYaml(unittest.TestCase):
return_value=self.calibration_definitions
):
self.composition_yaml = CompositionYaml(
self.build_cfg, self.zc_spec, self.unit_cfg, self.zc_core, self.zc_dids, {}
self.build_cfg, self.zc_spec, self.unit_cfg, self.zc_core, self.zc_dids, self.nvm_def, {}
)
result = self.composition_yaml.gather_yaml_info()
self.assertDictEqual(composition_yaml_with_calls_all_fields.expected_result, result)
@ -205,7 +210,7 @@ class TestCompositionYaml(unittest.TestCase):
return_value=self.calibration_definitions
):
self.composition_yaml = CompositionYaml(
self.build_cfg, self.zc_spec, self.unit_cfg, self.zc_core, self.zc_dids, {}
self.build_cfg, self.zc_spec, self.unit_cfg, self.zc_core, self.zc_dids, self.nvm_def, {}
)
result = self.composition_yaml.gather_yaml_info()
self.assertDictEqual(composition_yaml_with_calls_no_optional_fields.expected_result, result)
@ -220,7 +225,7 @@ class TestCompositionYaml(unittest.TestCase):
return_value=self.calibration_definitions
):
self.composition_yaml = CompositionYaml(
self.build_cfg, self.zc_spec, self.unit_cfg, self.zc_core, self.zc_dids, {}
self.build_cfg, self.zc_spec, self.unit_cfg, self.zc_core, self.zc_dids, self.nvm_def, {}
)
result = self.composition_yaml.gather_yaml_info()
self.assertDictEqual(composition_yaml_with_dids.expected_result, result)
@ -235,11 +240,26 @@ class TestCompositionYaml(unittest.TestCase):
return_value=self.calibration_definitions
):
self.composition_yaml = CompositionYaml(
self.build_cfg, self.zc_spec, self.unit_cfg, self.zc_core, self.zc_dids, {}
self.build_cfg, self.zc_spec, self.unit_cfg, self.zc_core, self.zc_dids, self.nvm_def, {}
)
result = self.composition_yaml.gather_yaml_info()
self.assertDictEqual(composition_yaml_with_dtcs.expected_result, result)
def test_composition_yaml_with_nvm(self):
"""Checking that the dict is generated correctly, with NVM."""
self.nvm_def.nvm_definitions = composition_yaml_with_nvm.project_nvm_definitions
self.zc_spec["nv-needs"] = composition_yaml_with_nvm.yaml_nvm_definitions
with patch.object(
CompositionYaml,
"_get_all_calibration_definitions",
return_value=self.calibration_definitions
):
self.composition_yaml = CompositionYaml(
self.build_cfg, self.zc_spec, self.unit_cfg, self.zc_core, self.zc_dids, self.nvm_def, {}
)
result = self.composition_yaml.gather_yaml_info()
self.assertDictEqual(composition_yaml_with_nvm.expected_result, result)
def test_get_init_values_expecting_failure(self):
"""Test CompositionYaml.get_init_values with a non-existing calibration definition."""
self.composition_yaml.clear_log()