From ed73202f9d3d7da29c4fa5444c4ceb91437e42c8 Mon Sep 17 00:00:00 2001 From: Henrik Wahlqvist Date: Tue, 4 Mar 2025 09:26:01 +0100 Subject: [PATCH] Add option for redirecting calibration variables as RTE function calls Change-Id: Iacc4ea5188c01dd1c030b1b0606658177130e322 --- docs/project_config.md | 10 +++ powertrain_build/build.py | 17 ++-- powertrain_build/build_proj_config.py | 1 + powertrain_build/memory_section.py | 30 ++++++- .../zone_controller/composition_yaml.py | 7 +- tests/powertrain_build/test_memory_section.py | 51 +++++++++-- .../zone_controller/test_composition_yaml.py | 85 +++++++++++++++---- 7 files changed, 173 insertions(+), 28 deletions(-) diff --git a/docs/project_config.md b/docs/project_config.md index 06a5811..198035a 100644 --- a/docs/project_config.md +++ b/docs/project_config.md @@ -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\\_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. diff --git a/powertrain_build/build.py b/powertrain_build/build.py index 0aaca6f..dc307b1 100644 --- a/powertrain_build/build.py +++ b/powertrain_build/build.py @@ -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("******************************************************") diff --git a/powertrain_build/build_proj_config.py b/powertrain_build/build_proj_config.py index e07e3da..1015f68 100644 --- a/powertrain_build/build_proj_config.py +++ b/powertrain_build/build_proj_config.py @@ -67,6 +67,7 @@ class BuildProjConfig: 'generalAsilLevelDebug': 'B', 'generalAsilLevelDependability': 'B', 'generateCalibrationInterfaceFiles': False, + 'useCalibrationRteMacroExpansion': False, 'generateCoreDummy': False, 'generateDummyVar': False, 'generateInterfaceHeaders': False, diff --git a/powertrain_build/memory_section.py b/powertrain_build/memory_section.py index 8c56c4b..483cbc3 100644 --- a/powertrain_build/memory_section.py +++ b/powertrain_build/memory_section.py @@ -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: diff --git a/powertrain_build/zone_controller/composition_yaml.py b/powertrain_build/zone_controller/composition_yaml.py index f5769d3..1b5856f 100644 --- a/powertrain_build/zone_controller/composition_yaml.py +++ b/powertrain_build/zone_controller/composition_yaml.py @@ -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} diff --git a/tests/powertrain_build/test_memory_section.py b/tests/powertrain_build/test_memory_section.py index da69e4f..c95dc48 100644 --- a/tests/powertrain_build/test_memory_section.py +++ b/tests/powertrain_build/test_memory_section.py @@ -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' diff --git a/tests/zone_controller/test_composition_yaml.py b/tests/zone_controller/test_composition_yaml.py index e1fe130..e6df470 100644 --- a/tests/zone_controller/test_composition_yaml.py +++ b/tests/zone_controller/test_composition_yaml.py @@ -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 = \