diff --git a/powertrain_build/build.py b/powertrain_build/build.py index 55f8ce1..9054bf9 100644 --- a/powertrain_build/build.py +++ b/powertrain_build/build.py @@ -34,6 +34,7 @@ from powertrain_build.memory_section import MemorySection from powertrain_build.nvm_def import NVMDef, ZCNVMDef from powertrain_build.problem_logger import ProblemLogger from powertrain_build.replace_compu_tab_ref import replace_tab_verb +from powertrain_build.rte_dummy import RteDummy from powertrain_build.sched_funcs import SchedFuncs from powertrain_build.signal_if_html_rep import SigIfHtmlReport from powertrain_build.signal_incons_html_rep import SigConsHtmlReport @@ -669,6 +670,11 @@ def add_args(parser): action="store_true", help="Generate core dummy code to enable integration with old supplier code", ) + powertrain_build_parser.add_argument( + "--rte-dummy", + action="store_true", + help="Generate RTE dummy code to enable e.g. Silver testing", + ) powertrain_build_parser.add_argument( "--debug", action="store_true", help="Activate the debug log" ) @@ -708,6 +714,7 @@ def build( project_config, interface=False, core_dummy=False, + rte_dummy=False, no_abort=False, no_nvm_a2l=False, debug=False, @@ -724,6 +731,7 @@ def build( project_config (str): Project configuration file. interface (bool): Generate interface report. Default=False. core_dummy (bool): Generate core dummy code. Default=False. + rte_dummy (bool): Generate RTE dummy code. Default=False. no_abort (bool): Do not abort due to errors. Default=False. no_nvm_a2l (bool): Do not generate A2L for NVM structs. Default=False. debug (bool): Activate the debug log. Default=False. @@ -921,9 +929,15 @@ def build( LOG.info("Generating DID files") zc_dids.generate_did_files() LOG.info("******************************************************") - LOG.info("Start generating NVMDefinitions") + LOG.info("Generating NVM definitions") zc_nvm.generate_nvm_rte_files() + if rte_dummy: + LOG.info("******************************************************") + LOG.info("Generating RTE dummy files") + zc_rte = RteDummy(build_cfg, zc_nvm) + zc_rte.generate_rte_dummy() + if code_generation_config["generateCalibrationInterfaceFiles"]: LOG.info("******************************************************") LOG.info("Generating calibration interface files") diff --git a/powertrain_build/rte_dummy.py b/powertrain_build/rte_dummy.py new file mode 100644 index 0000000..8a838e8 --- /dev/null +++ b/powertrain_build/rte_dummy.py @@ -0,0 +1,132 @@ +# Copyright 2024 Volvo Car Corporation +# Licensed under Apache 2.0. + +# -*- coding: utf-8 -*- +"""Module containing classes for generating RTE dummy code. + +These files are needed for building test SW, +where the RTE is not available. +For example, when running Silver tests. +""" + +from powertrain_build.problem_logger import ProblemLogger + +from pathlib import Path + + +class RteDummy(ProblemLogger): + """A class for RTE dummy file generation.""" + + def __init__(self, build_cfg, nvm_def): + """Init. + + Args: + build_cfg (BuildProjConfig): Object with build configuration settings. + nvm_def (ZCNVMDef): Object with NVM definition information. + """ + super().__init__() + self.build_cfg = build_cfg + self.nvm_def = nvm_def + self.header_file_name = "Rte_Type" + self.source_file_name = "Rte_Dummy" + nvm_port_pattern = self.build_cfg.get_composition_config("nvmPortPattern") + if nvm_port_pattern is None: + nvm_port_pattern = "{NvName}" + self.nvm_port_pattern = nvm_port_pattern + + def _get_header_header(self): + """Get header for the RTE dummy header.""" + return ( + "/*\n" + " * This file is generated by the Powertrain Build System.\n" + " * It defines RTE dummy types.\n" + " * Do not modify this file manually.\n" + " */\n" + f"#ifndef {self.header_file_name.upper()}_H\n" + f"#define {self.header_file_name.upper()}_H\n\n" + '#include "tl_basetypes.h"\n\n' + "#define FALSE 0U\n" + "#define TRUE 1U\n\n" + ) + + def _get_header_footer(self): + """Get footer for the RTE dummy header.""" + return f"\n#endif /* {self.header_file_name.upper()}_H */\n" + + def _get_source_header(self): + """Get header for the RTE dummy source.""" + return ( + "/*\n" + " * This file is generated by the Powertrain Build System.\n" + " * It defines RTE dummy functions.\n" + " * Do not modify this file manually.\n" + " */\n" + f'#include "{self.header_file_name}.h"\n\n' + ) + + def _get_nvm_header_dummy(self): + """Get NVM dummy header code.""" + struct_defines = [] + function_declarations = [] + prefix = self.build_cfg.get_scheduler_prefix() + + for memory_area in self.nvm_def._nvm_memory_areas: + nvm_name = f"{prefix}{memory_area}" + function_prefix = f"Rte_Call_{self.nvm_port_pattern.format(NvName=nvm_name)}" + function_declarations.append(f"dt_{nvm_name} *Rte_Pim_{nvm_name}(void);") + function_declarations.append(f"void {function_prefix}_SetRamBlockStatus(UInt8 status);") + function_declarations.append(f"void {function_prefix}_GetErrorStatus(UInt8 *status);") + function_declarations.append(f"void {function_prefix}_WriteBlock(dt_{nvm_name} *block);") + + if self.build_cfg.get_code_generation_config("useRteNvmStructs"): + struct_defines.append("typedef struct\n{") + memory_area_index = self.nvm_def._get_nvm_areas_index(memory_area) + nr_of_unused_signals = self.nvm_def.nvm_definitions[memory_area_index]["size"] + signals = self.nvm_def.nvm_definitions[memory_area_index]["signals"] + for signal in signals: + signal_string = "" + nr_of_unused_signals -= signal["x_size"] * signal["y_size"] + signal_string += f' {signal["type"]} {self.nvm_def.struct_member_prefix}{signal["name"]}' + size = max(signal["x_size"], 1) * max(signal["y_size"], 1) + if size > 1: + if signal["x_size"] > 1: + signal_string += f'[{signal["x_size"]}]' + if signal["y_size"] > 1: + signal_string += f'[{signal["y_size"]}]' + signal_string += ";" + struct_defines.append(signal_string) + if nr_of_unused_signals > 0: + struct_defines.append( + f' {self.nvm_def.nvm_definitions[memory_area_index]["default_datatype"]} ' + f'unused[{nr_of_unused_signals}];' + ) + struct_defines.append(f"}} dt_{nvm_name};\n") + + return "\n".join(struct_defines + function_declarations) + + def _generate_nvm_source_dummy(self): + """Generate NVM source dummy code.""" + lines_to_write = [] + prefix = self.build_cfg.get_scheduler_prefix() + for memory_area in self.nvm_def._nvm_memory_areas: + nvm_name = f"{prefix}{memory_area}" + function_prefix = f"Rte_Call_{self.nvm_port_pattern.format(NvName=nvm_name)}" + lines_to_write.append(f"dt_{nvm_name} *Rte_Pim_{nvm_name}(void) {{ return (dt_{nvm_name} *)0; }}") + lines_to_write.append(f"void {function_prefix}_SetRamBlockStatus(UInt8 status) {{}}") + lines_to_write.append(f"void {function_prefix}_GetErrorStatus(UInt8 *status) {{}}") + lines_to_write.append(f"void {function_prefix}_WriteBlock(dt_{nvm_name} *block) {{}}") + lines_to_write.append("") + return "\n".join(lines_to_write) + + def generate_rte_dummy(self): + """Generate RTE dummy files.""" + src_code_dest_dir = self.build_cfg.get_src_code_dst_dir() + header_file = Path(src_code_dest_dir, self.header_file_name + ".h") + source_file = Path(src_code_dest_dir, self.source_file_name + ".c") + with header_file.open(mode="w", encoding="utf-8") as header_fh: + header_fh.write(self._get_header_header()) + header_fh.write(self._get_nvm_header_dummy()) + header_fh.write(self._get_header_footer()) + with source_file.open(mode="w", encoding="utf-8") as source_fh: + source_fh.write(self._get_source_header()) + source_fh.write(self._generate_nvm_source_dummy()) diff --git a/powertrain_build/wrapper.py b/powertrain_build/wrapper.py index 742bbcb..57555c4 100644 --- a/powertrain_build/wrapper.py +++ b/powertrain_build/wrapper.py @@ -56,6 +56,7 @@ class PyBuildWrapper(pt_matlab.Matlab): self.project_config = self._set_project_configuration(args) self.generate_system_info = getattr(args, "generate_system_info", False) self.core_dummy = getattr(args, "core_dummy", True) + self.rte_dummy = getattr(args, "rte_dummy", False) self.debug = getattr(args, "debug", True) self.no_abort = getattr(args, "no_abort", True) self.no_nvm_a2l = getattr(args, "no_nvm_a2l", False) @@ -374,6 +375,7 @@ class PyBuildWrapper(pt_matlab.Matlab): self.project_config, interface=self.interface, core_dummy=self.core_dummy, + rte_dummy=self.rte_dummy, no_abort=self.no_abort, no_nvm_a2l=self.no_nvm_a2l, debug=self.debug, diff --git a/tests/powertrain_build/test_rte_dummy.py b/tests/powertrain_build/test_rte_dummy.py new file mode 100644 index 0000000..46a88a1 --- /dev/null +++ b/tests/powertrain_build/test_rte_dummy.py @@ -0,0 +1,122 @@ +# Copyright 2024 Volvo Car Corporation +# Licensed under Apache 2.0. + +"""Unit test script for powertrain_build.rte_dummy.""" + +import unittest +from pathlib import Path +from unittest.mock import MagicMock, patch, mock_open + +from powertrain_build.rte_dummy import RteDummy + + +def mock_get_nvm_areas_index(memory_area): + """Return the index of the given memory area.""" + return {"NVM_LIST_8": 0, "NVM_LIST_16": 1}[memory_area] + + +class TestRteDummy(unittest.TestCase): + """Class for testing powertrain_build.rte_dummy.""" + + def setUp(self): + """Set-up mocks and common variables and data structures for all tests in the test case.""" + self.build_cfg = MagicMock() + self.build_cfg.get_scheduler_prefix.return_value = "DUMMY_" + self.build_cfg.get_composition_config.return_value = "PS_DUMMY_SwcNv_{NvName}" + self.nvm_def = MagicMock() + self.nvm_def._get_nvm_areas_index.side_effect = mock_get_nvm_areas_index + self.nvm_def.struct_member_prefix = "e_" + self.nvm_def._nvm_memory_areas = ("NVM_LIST_8", "NVM_LIST_16") + self.nvm_def.nvm_definitions = [ + { + "name": "NVM_LIST_8", + "allowed_datatypes": ["Bool", "UInt8", "Int8"], + "size": 2, + "instanceName": "nvm_list_8", + "default_datatype": "UInt8", + "includeStop": "", + "includeStart": "", + "persistent": False, + "signals": [ + {"name": "dummy", "type": "UInt8", "x_size": 1, "y_size": 1} + ] + }, + { + "name": "NVM_LIST_16", + "allowed_datatypes": ["UInt16", "Int16"], + "size": 2, + "instanceName": "nvm_list_16", + "default_datatype": "UInt16", + "includeStop": "", + "includeStart": "", + "persistent": False, + "signals": [ + {"name": "dummy2", "type": "UInt16", "x_size": 1, "y_size": 1} + ] + } + ] + self.rte_dummy = RteDummy(self.build_cfg, self.nvm_def) + + def test_generate_rte_dummy(self): + """Test RteDummy.generate_rte_dummy().""" + result = [] + m_open = mock_open() + m_open.return_value.write = result.append + with patch.object(Path, "open", m_open, create=True): + self.rte_dummy.generate_rte_dummy() + expected = [ + ( # header header + "/*\n" + " * This file is generated by the Powertrain Build System.\n" + " * It defines RTE dummy types.\n" + " * Do not modify this file manually.\n" + " */\n" + "#ifndef RTE_TYPE_H\n" + "#define RTE_TYPE_H\n\n" + "#include \"tl_basetypes.h\"\n\n" + "#define FALSE 0U\n" + "#define TRUE 1U\n\n" + ), + ( # header content + "typedef struct\n" + "{\n" + " UInt8 e_dummy;\n" + " UInt8 unused[1];\n" + "} dt_DUMMY_NVM_LIST_8;\n\n" + "typedef struct\n" + "{\n" + " UInt16 e_dummy2;\n" + " UInt16 unused[1];\n" + "} dt_DUMMY_NVM_LIST_16;\n\n" + "dt_DUMMY_NVM_LIST_8 *Rte_Pim_DUMMY_NVM_LIST_8(void);\n" + "void Rte_Call_PS_DUMMY_SwcNv_DUMMY_NVM_LIST_8_SetRamBlockStatus(UInt8 status);\n" + "void Rte_Call_PS_DUMMY_SwcNv_DUMMY_NVM_LIST_8_GetErrorStatus(UInt8 *status);\n" + "void Rte_Call_PS_DUMMY_SwcNv_DUMMY_NVM_LIST_8_WriteBlock(dt_DUMMY_NVM_LIST_8 *block);\n" + "dt_DUMMY_NVM_LIST_16 *Rte_Pim_DUMMY_NVM_LIST_16(void);\n" + "void Rte_Call_PS_DUMMY_SwcNv_DUMMY_NVM_LIST_16_SetRamBlockStatus(UInt8 status);\n" + "void Rte_Call_PS_DUMMY_SwcNv_DUMMY_NVM_LIST_16_GetErrorStatus(UInt8 *status);\n" + "void Rte_Call_PS_DUMMY_SwcNv_DUMMY_NVM_LIST_16_WriteBlock(dt_DUMMY_NVM_LIST_16 *block);" + ), + ( # header footer + "\n#endif /* RTE_TYPE_H */\n" + ), + ( # source header + "/*\n" + " * This file is generated by the Powertrain Build System.\n" + " * It defines RTE dummy functions.\n" + " * Do not modify this file manually.\n" + " */\n" + "#include \"Rte_Type.h\"\n\n" + ), + ( # source content + "dt_DUMMY_NVM_LIST_8 *Rte_Pim_DUMMY_NVM_LIST_8(void) { return (dt_DUMMY_NVM_LIST_8 *)0; }\n" + "void Rte_Call_PS_DUMMY_SwcNv_DUMMY_NVM_LIST_8_SetRamBlockStatus(UInt8 status) {}\n" + "void Rte_Call_PS_DUMMY_SwcNv_DUMMY_NVM_LIST_8_GetErrorStatus(UInt8 *status) {}\n" + "void Rte_Call_PS_DUMMY_SwcNv_DUMMY_NVM_LIST_8_WriteBlock(dt_DUMMY_NVM_LIST_8 *block) {}\n" + "dt_DUMMY_NVM_LIST_16 *Rte_Pim_DUMMY_NVM_LIST_16(void) { return (dt_DUMMY_NVM_LIST_16 *)0; }\n" + "void Rte_Call_PS_DUMMY_SwcNv_DUMMY_NVM_LIST_16_SetRamBlockStatus(UInt8 status) {}\n" + "void Rte_Call_PS_DUMMY_SwcNv_DUMMY_NVM_LIST_16_GetErrorStatus(UInt8 *status) {}\n" + "void Rte_Call_PS_DUMMY_SwcNv_DUMMY_NVM_LIST_16_WriteBlock(dt_DUMMY_NVM_LIST_16 *block) {}\n" + ) + ] + self.assertListEqual(result, expected)