Add option for redirecting calibration variables as RTE function calls

Change-Id: Iacc4ea5188c01dd1c030b1b0606658177130e322
This commit is contained in:
Henrik Wahlqvist 2025-03-04 09:26:01 +01:00
parent 9fc9a2f40d
commit ed73202f9d
7 changed files with 173 additions and 28 deletions

View File

@ -118,6 +118,7 @@ This key is used to set individual options to match the old ECU types, see examp
"generalAsilLevelDebug": "B",
"generalAsilLevelDependability": "B",
"generateCalibrationInterfaceFiles": false,
"useCalibrationRteMacroExpansion": false,
"generateCoreDummy": true,
"generateDummyVar": true,
"generateInterfaceHeaders": false,
@ -154,6 +155,15 @@ These files can be used together with a certain type of calibration strategy in
The files generates a certain variable called "c\<swc_name\>\_TriggerReadRteCData" which can be toggled to read the rest of the calibration variables.
This makes the CPU not overload reading all the calibration variables from the RTE every iteration.
#### useCalibrationRteMacroExpansion
Redefine calibration variables as RTE calls.
Takes priority over "generateCalibrationInterfaceFiles" and is only available when "generateYamlInterfaceFile" is true.
Default is False.
Redefines calibration variables as RTE calls in an attempty to save memory.
Note that this increases CPU load.
#### generateCoreDummy
Generates a file defining dummy versions of functions towards e.g. supplier interface type functions.

View File

@ -888,7 +888,7 @@ def build(
copy_unit_cfgs_to_output(build_cfg)
copy_files_to_include(build_cfg)
if code_generation_config["generateInterfaceHeaders"]:
memory_section = MemorySection(build_cfg)
memory_section = MemorySection(build_cfg, unit_cfg)
memory_section.generate_required_header_files()
# Propagate tag name for release builds, TAG_NAME must be set in environment
@ -944,11 +944,16 @@ def build(
if code_generation_config["generateCalibrationInterfaceFiles"]:
LOG.info("******************************************************")
LOG.info("Generating calibration interface files")
zc_calibration = ZoneControllerCalibration(
build_cfg, composition_yaml.cal_class_info["tl"]
)
zc_calibration.generate_calibration_interface_files()
if code_generation_config["useCalibrationRteMacroExpansion"]:
LOG.warning(
"Skip generating calibration interface files as useCalibrationRteMacroExpansion is set to true"
)
else:
LOG.info("Generating calibration interface files")
zc_calibration = ZoneControllerCalibration(
build_cfg, composition_yaml.cal_class_info["tl"]
)
zc_calibration.generate_calibration_interface_files()
elif build_cfg.get_ecu_info()[0] == "HI":
generate_nvm_def(build_cfg, unit_cfg, no_nvm_a2l)
LOG.info("******************************************************")

View File

@ -67,6 +67,7 @@ class BuildProjConfig:
'generalAsilLevelDebug': 'B',
'generalAsilLevelDependability': 'B',
'generateCalibrationInterfaceFiles': False,
'useCalibrationRteMacroExpansion': False,
'generateCoreDummy': False,
'generateDummyVar': False,
'generateInterfaceHeaders': False,

View File

@ -32,9 +32,10 @@ class MemorySection(ProblemLogger):
'CVC_DISP_ASIL_D'
]
def __init__(self, build_cfg):
def __init__(self, build_cfg, unit_cfg):
super().__init__()
self.build_cfg = build_cfg
self.unit_cfg = unit_cfg
self.mem_map_config = self.build_cfg.get_memory_map_config()
self.include_header_guards = self.mem_map_config['includeHeaderGuards']
self.mem_map_include = f'#include "{self.mem_map_config["memMapPrefix"]}_MemMap.h"\n'
@ -58,6 +59,27 @@ class MemorySection(ProblemLogger):
section_file_header_guard = section_file.split('.')[0].upper()
return [f'\n#endif /* {section_file_header_guard}_H */\n']
def _get_calibration_rte_macro_expansion(self, section_file):
macros = self._get_header(section_file)
swc_name = self.build_cfg.get_composition_config("softwareComponentName")
macros.append(f'#include "Rte_{swc_name}.h"\n')
config = self.unit_cfg.get_per_cfg_unit_cfg()
valid_configs = ["outports", "local_vars", "calib_consts"]
for valid_config in valid_configs:
for signal_name, unit_info in config.get(valid_config, {}).items():
define_str = f'#define {signal_name} Rte_CData_{swc_name}_{signal_name}()'
if signal_name.startswith("m") and not signal_name.endswith("_r") and not signal_name.endswith("_c"):
define_str += f"->dt_{signal_name}"
define_str += "\n"
for info in unit_info.values(): # Should be length 1 for cal
if "CVC_CAL" in info["class"]:
macros.append(define_str)
macros.extend(self._get_footer(section_file))
return macros
def _get_cal(self, section):
cvc_undefines = [f'#undef {definition}\n' for definition in self.calibration_definitions]
if section == 'START':
@ -149,6 +171,12 @@ class MemorySection(ProblemLogger):
self.critical('Should not find CVC_CAL/DISP here. Check logic. File: %s.', section_file)
elif section_file.startswith('CVC_CODE'):
cvc_undefines, cvc_defines, memory_section_handling = self._get_code(section)
use_rte_macro_expansion = self.build_cfg.get_code_generation_config(item="useRteMacroExpansion")
if section == 'START' and use_rte_macro_expansion:
memory_section_handling.extend(self._get_calibration_rte_macro_expansion(section_file))
# header and footer are part of the extension
header = []
footer = []
elif section_file.startswith('CVC_CONST'):
cvc_undefines, cvc_defines, memory_section_handling = self._get_const(section)
else:

View File

@ -57,7 +57,12 @@ class CompositionYaml(ProblemLogger):
self.calibration_init_values = self.get_init_values(calibration_variables)
self.cal_class_info = self._get_class_info(calibration_variables)
self.meas_class_info = self._get_class_info(measurable_variables)
self.include_calibration_interface_files = False
if self.build_cfg.get_code_generation_config(item="generateCalibrationInterfaceFiles"):
self.include_calibration_interface_files = True
if self.build_cfg.get_code_generation_config(item="useCalibrationRteMacroExpansion"):
self.include_calibration_interface_files = False
if self.include_calibration_interface_files:
trigger_read_rte_cdata_signal_name = self._get_calibration_trigger_signal_name(calibration_variables)
self.cal_class_info["autosar"]["class_info"].update(
{
@ -456,7 +461,7 @@ class CompositionYaml(ProblemLogger):
calibration_variables = list(self.cal_class_info["autosar"]["class_info"].keys())
swc_content = {init_function: {"type": "INIT", "accesses": calibration_variables}}
if self.build_cfg.get_code_generation_config(item="generateCalibrationInterfaceFiles"):
if self.include_calibration_interface_files:
cal_init_function = autosar_prefix + ZCC.calibration_function_init_template.format(swc_name=swc_name)
cal_step_function = autosar_prefix + ZCC.calibration_function_step_template.format(swc_name=swc_name)
swc_content[cal_init_function] = {"type": "INIT", "accesses": calibration_variables}

View File

@ -7,6 +7,7 @@ from pathlib import Path
from unittest import mock, TestCase
from powertrain_build.build_proj_config import BuildProjConfig
from powertrain_build.memory_section import MemorySection
from powertrain_build.unit_configs import UnitConfigs
from powertrain_build.lib import helper_functions
from .test_build import remove
@ -22,10 +23,12 @@ class TestMemorySection(TestCase):
helper_functions.create_dir(self.src_code_dir)
cnfg_files_folder = Path(SRC_DIR, 'cnfg_files')
build_cfg = mock.MagicMock(spec_set=BuildProjConfig(Path(cnfg_files_folder, 'ProjectCfg.json')))
build_cfg.get_src_code_dst_dir = mock.MagicMock(return_value=self.src_code_dir)
build_cfg.get_use_volatile_globals = mock.MagicMock(return_value=False)
build_cfg.get_memory_map_config = mock.MagicMock(
self.build_cfg = mock.MagicMock(spec_set=BuildProjConfig(Path(cnfg_files_folder, 'ProjectCfg.json')))
self.build_cfg.get_composition_config = mock.MagicMock(return_value='DUMMY')
self.build_cfg.get_code_generation_config = mock.MagicMock(return_value=False)
self.build_cfg.get_src_code_dst_dir = mock.MagicMock(return_value=self.src_code_dir)
self.build_cfg.get_use_volatile_globals = mock.MagicMock(return_value=False)
self.build_cfg.get_memory_map_config = mock.MagicMock(
return_value={
'includeHeaderGuards': True,
'includeMemMapForCalibration': False,
@ -46,7 +49,8 @@ class TestMemorySection(TestCase):
}
}
)
self.memory_section = MemorySection(build_cfg)
self.unit_cfg = mock.MagicMock(spec_set=UnitConfigs)
self.memory_section = MemorySection(self.build_cfg, self.unit_cfg)
def test_generate_cvc_header_cal(self):
"""Test MemorySection.generate_cvc_header() with CAL type file."""
@ -240,6 +244,43 @@ class TestMemorySection(TestCase):
self.assertListEqual(expected_start, result_start)
self.assertListEqual(expected_stop, result_stop)
def test_generate_cvc_header_code_with_rte_macros(self):
"""Test MemorySection.generate_cvc_header(),
with CODE type file and useCalibrationRteMacroExpansion set to true."""
self.build_cfg.get_code_generation_config = mock.MagicMock(return_value=True)
self.unit_cfg.get_per_cfg_unit_cfg.return_value = {
'calib_consts': {
'mVcDummyOne': {'DummyUnit': {'class': 'CVC_CAL'}}
},
'outports': {
'sVcDummyOne': {'DummyUnit': {'class': 'CVC_DISP'}},
'sVcDummyTwo': {'DummyUnit': {'class': 'CVC_CAL'}}
}
}
self.memory_section = MemorySection(self.build_cfg, self.unit_cfg)
test_file_name = 'CVC_CODE_DUMMY.h'
expected_start = [
'#define MOCK_HI_START_SEC_CODE\n',
'#include "MOCK_HI_MemMap.h"\n',
'#ifndef CVC_CODE_DUMMY_H\n',
'#define CVC_CODE_DUMMY_H\n\n',
'#include "Rte_DUMMY.h"\n',
'#define sVcDummyTwo Rte_CData_DUMMY_sVcDummyTwo()\n',
'#define mVcDummyOne Rte_CData_DUMMY_mVcDummyOne()->dt_mVcDummyOne\n',
'\n#endif /* CVC_CODE_DUMMY_H */\n'
]
expected_stop = [
'#ifndef CVC_CODE_DUMMY_H\n',
'#define CVC_CODE_DUMMY_H\n\n',
'#define MOCK_HI_STOP_SEC_CODE\n',
'#include "MOCK_HI_MemMap.h"\n',
'\n#endif /* CVC_CODE_DUMMY_H */\n'
]
result_start = self.memory_section.generate_cvc_header('START', test_file_name)
result_stop = self.memory_section.generate_cvc_header('END', test_file_name)
self.assertListEqual(expected_start, result_start)
self.assertListEqual(expected_stop, result_stop)
def test_generate_cvc_header_const(self):
"""Test MemorySection.generate_cvc_header() with CONST type file."""
test_file_name = 'CVC_CONST_DUMMY.h'

View File

@ -33,12 +33,52 @@ class BuildProjConfigMock(BuildProjConfig):
name = ""
def mock_get_code_generation_config_default(item):
"""Function to mock BuildProjConfig.get_code_generation_config."""
return {
"generalAsilLevelDebug": "B",
"generalAsilLevelDependability": "B",
"generateCalibrationInterfaceFiles": False,
"useCalibrationRteMacroExpansion": False,
"generateCoreDummy": False,
"generateDummyVar": False,
"generateInterfaceHeaders": False,
"generateRteCheckpointIds": False,
"generateYamlInterfaceFile": False,
"includeAllEnums": False,
"mapToRteEnums": False,
"propagateTagName": False,
"useA2lSymbolLinks": False,
"useRteNvmStructs": False,
}[item]
def mock_get_code_generation_config_calibration_interface(item):
"""Function to mock BuildProjConfig.get_code_generation_config."""
return {
"generalAsilLevelDebug": "B",
"generalAsilLevelDependability": "B",
"generateCalibrationInterfaceFiles": True,
"useCalibrationRteMacroExpansion": False,
"generateCoreDummy": False,
"generateDummyVar": False,
"generateInterfaceHeaders": False,
"generateRteCheckpointIds": False,
"generateYamlInterfaceFile": False,
"includeAllEnums": False,
"mapToRteEnums": False,
"propagateTagName": False,
"useA2lSymbolLinks": False,
"useRteNvmStructs": False,
}[item]
def mock_get_composition_config_default(key):
"""Function to mock BuildProjConfig.get_composition_config."""
return {
"compositionArxml": "some_arxml.arxml",
"compositionName": "compositionName",
'compositionEnding': 'yml',
"compositionEnding": "yml",
"softwareComponentName": "testName_SC",
"softwareComponentTemplate": "ARTCSC",
"asil": "QM",
@ -46,11 +86,11 @@ def mock_get_composition_config_default(key):
"customYamlInitFunctionName": None,
"customYamlStepFunctionName": None,
"generateExternalImplementationType": True,
'includeStatic': True,
'includeShared': True,
'includeDiagnostics': True,
'includeNvm': True,
'scaleMapsAndCurves': True,
"includeStatic": True,
"includeShared": True,
"includeDiagnostics": True,
"includeNvm": True,
"scaleMapsAndCurves": True,
}[key]
@ -59,7 +99,7 @@ def mock_get_composition_config_custom_names(key):
return {
"compositionArxml": "some_arxml.arxml",
"compositionName": "compositionName",
'compositionEnding': 'yml',
"compositionEnding": "yml",
"softwareComponentName": "testName_SC",
"softwareComponentTemplate": "ARTCSC",
"asil": "QM",
@ -67,11 +107,11 @@ def mock_get_composition_config_custom_names(key):
"customYamlInitFunctionName": "dummy_init",
"customYamlStepFunctionName": "dummy_step",
"generateExternalImplementationType": True,
'includeStatic': True,
'includeShared': True,
'includeDiagnostics': True,
'includeNvm': True,
'scaleMapsAndCurves': True,
"includeStatic": True,
"includeShared": True,
"includeDiagnostics": True,
"includeNvm": True,
"scaleMapsAndCurves": True,
}[key]
@ -81,6 +121,7 @@ class TestCompositionYaml(unittest.TestCase):
def setUp(self):
"""Set-up common data structures for all tests in the test case."""
self.build_cfg = MagicMock(spec_set=BuildProjConfigMock)
self.build_cfg.get_code_generation_config.side_effect = mock_get_code_generation_config_default
self.build_cfg.get_composition_config.side_effect = mock_get_composition_config_default
self.build_cfg.name = "XVC"
self.build_cfg.get_scheduler_prefix = MagicMock(return_value="prefix_")
@ -90,7 +131,6 @@ class TestCompositionYaml(unittest.TestCase):
self.build_cfg.get_units_raster_cfg = MagicMock(
return_value=({"SampleTimes": {"testRunnable": 10}})
)
self.build_cfg.get_code_generation_config = MagicMock(return_value=False)
self.unit_cfg = MagicMock(spec_set=UnitConfigs)
self.unit_cfg.get_per_cfg_unit_cfg.return_value = copy.deepcopy(
@ -145,8 +185,8 @@ class TestCompositionYaml(unittest.TestCase):
def test_composition_yaml_with_calibration(self):
"""Checking that the dict is generated correctly including calibration data,
setting generateCalibrationInterfaceFiles to true (sort of)."""
self.build_cfg.get_code_generation_config = MagicMock(return_value=True)
setting generateCalibrationInterfaceFiles to true."""
self.build_cfg.get_code_generation_config.side_effect = mock_get_code_generation_config_calibration_interface
with patch.object(
CompositionYaml,
"_get_all_calibration_definitions",
@ -158,6 +198,21 @@ class TestCompositionYaml(unittest.TestCase):
result = self.composition_yaml.gather_yaml_info()
self.assertDictEqual(composition_yaml.expected_cal_result, result)
def test_composition_yaml_with_calibration_and_rte_macro(self):
"""Checking that the dict is generated correctly including calibration data,
setting both generateCalibrationInterfaceFiles and useCalibrationRteMacroExpansion to true (sort of)."""
self.build_cfg.get_code_generation_config = MagicMock(return_value=True)
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.expected_result, result)
def test_composition_yaml_with_a2l_axis_data(self):
"""Checking that the dict is generated correctly, including a2l axis data."""
self.unit_cfg.get_per_cfg_unit_cfg.return_value = \